diff --git a/1-js/01-getting-started/1-intro/article.md b/1-js/01-getting-started/1-intro/article.md index 0c0f847daa..b2c304475a 100644 --- a/1-js/01-getting-started/1-intro/article.md +++ b/1-js/01-getting-started/1-intro/article.md @@ -1,6 +1,6 @@ -# Javascript 入門 +# JavaScript 入門 -JavaScript について、何が特別なのか、それを使って達成できることや他のどの技術と上手くやるのか見てみましょう。 +JavaScript について、何が特別なのか、それを使ってできることや他のどの技術と上手くやるのか見てみましょう。 ## JavaScript とは? @@ -8,119 +8,117 @@ JavaScript について、何が特別なのか、それを使って達成でき この言語のプログラムは *スクリプト* と呼ばれます。それらはHTMLの中に書かれ、ページが読み込まれると自動的に実行されます。 -スクリプトはプレーンテキストとして提供され、実行されます。 それらは特別な準備や、実行するためのコンパイルを必要としません。 +スクリプトはプレーンテキストとして提供され、実行されます。 特別な準備や、実行するためのコンパイルは必要ありません。 -この側面において、JavaScript は [Java](http://en.wikipedia.org/wiki/Java) とはとても異なります。 +この点において、JavaScript は [Java](http://en.wikipedia.org/wiki/Java) とは大きく異なります。 -```smart header="なぜ JavaScript?" +```smart header="なぜ JavaScript と言うのでしょう?" -JavaScript が作られたとき, 当初は別の名前を持っていました:"LiveScript"。しかし Java 言語はその時非常に人気がありました。そのため、Java の "弟" として新しい言語を位置づけることが助けになると判断されました。 -しかし、その進化により、JavaScriptは完全に独立した言語になり、[ECMAScript](http://en.wikipedia.org/wiki/ECMAScript)と呼ばれる自身の仕様を持ちました。今は、 Java とは全く関係はありません。 +JavaScript が作られたとき, 当初は別の名前を持っていました: "LiveScript"。しかし当時 Java 言語が非常に人気であったため、Java の "弟" として新しい言語を位置づけるのが良いと判断されました。 +しかし、それ以降の進化により、JavaScriptは完全に独立した言語になり、[ECMAScript](http://en.wikipedia.org/wiki/ECMAScript)と呼ばれる独自の仕様を持ちました。現在 Java とは全く関係ありません。 ``` -現在、JavaScript はブラウザだけでなく、サーバ上でも実行することができます。また、実際には [JavaScript エンジン](https://en.wikipedia.org/wiki/JavaScript_engine) と呼ばれるプログラムが存在するデバイスであれば実行することができます。 +現在、JavaScript はブラウザだけでなく、サーバ上でも実行することができます。実際には [JavaScript エンジン](https://en.wikipedia.org/wiki/JavaScript_engine) と呼ばれるプログラムが存在するデバイスであれば実行することができます。 -ブラウザにはエンジンが組み込まれており、それは "JavaScript 仮想マシン" と呼ばれる場合があります。 +ブラウザにはエンジンが組み込まれており、"JavaScript 仮想マシン" と呼ばれる場合があります。 異なるエンジンは異なる "コードネーム" を持っています。例えば: -- [V8](https://en.wikipedia.org/wiki/V8_(JavaScript_engine)) -- in Chrome and Opera. -- [SpiderMonkey](https://en.wikipedia.org/wiki/SpiderMonkey) -- in Firefox. -- ...IEの異なるバージョン用として "Trident" や "Chakra", Microsoft Edge 用の "ChakraCore", Safari 用の "Nitro" や "SquirrelFish" 等のように他のコードネームもあります。 +- [V8](https://en.wikipedia.org/wiki/V8_(JavaScript_engine)) -- Chrome と Opera. +- [SpiderMonkey](https://en.wikipedia.org/wiki/SpiderMonkey) -- Firefox. +- ...IEはバージョンによって "Trident" や "Chakra", Microsoft Edge 用の "ChakraCore", Safari 用の "Nitro" や "SquirrelFish" 等のように他のコードネームもあります。 -上記の用語は、インターネット上の開発者の記事で使用されているため、覚えておくと良いです。 我々もそれらを使うでしょう。たとえば、 "ある機能XがV8でサポートされている" 場合、おそらくChromeとOperaで動作します。 +これらの用語は、インターネット上の開発者の記事で使用されているため、覚えておくと良いでしょう。 たとえば、"ある機能 X がV8でサポートされている" と言った場合、おそらくChromeとOperaで動作します。 ```smart header="エンジンはどのように動く?" -エンジンは複雑です。 しかし、基本は簡単です。 +エンジンは複雑ですが、基本は単純です。 1. エンジン (ブラウザの場合は組み込まれています) はスクリプトを読み("パース")ます。 -2. そして、スクリプトをマシン語に変換("コンパイル")します。 -3. そして、マシンコードは非常に早く動く +2. その後、スクリプトを機械語に変換("コンパイル")します。 +3. 機械語が実行されます。非常に早く動作します。 -エンジンは、プロセスのすべてのステージで最適化を適用します。 -実行時にコンパイルされたスクリプトも見ており、その中を流れるデータを分析し、その知識に基づいてマシンコードに最適化を適用します。 最終的に、スクリプトはかなり高速です。 +エンジンは処理の各ステップで最適化を行います。実行時にコンパイルされたスクリプトも見ており、そこを流れるデータを分析し、それ基づいて機械語を最適化します。 最終的に、スクリプトはとても速く実行されます。 ``` -## ブラウザ内のJavaScriptは何をすることができる? +## ブラウザ内のJavaScriptができることは? -モダンなJavaScriptは "安全な" プログラミング言語です。 -それは、メモリやCPUのような低レベルのアクセスは提供しません。なぜなら、それは最初にそれを必要としないブラウザ用に作成されたからです。 +モダンなJavaScriptは "安全な" プログラミング言語です。それは、メモリやCPUのような低レベルのアクセスは提供しません。なぜなら、当初はそれらを必要としないブラウザ用に作成されたものだからです。 -この能力は、JavaScriptを実行する環境に大きく依存します。 例えば、[Node.JS](https://wikipedia.org/wiki/Node.js) は、JavaScriptが任意のファイルを読み書きできるようにする関数をサポートしています。 +JavaScript の機能は、実行される環境に大きく依存します。 例えば、[Node.js](https://wikipedia.org/wiki/Node.js) では、JavaScriptが任意のファイルを読み書きしたりできる機能をサポートしています。 -ブラウザ内のJavaScriptは、Webページ操作、ユーザやWebサーバとのやり取りに関するすべてのことができます。 +ブラウザ内のJavaScriptは、Webページの操作、ユーザやWebサーバとのやり取りに関する様々なことを実行できます。 -たとえば、ブラウザ内のJavaScriptは以下のことが可能です: +たとえば、ブラウザ内のJavaScriptは次のようなことが可能です: -- 新たなHTMLをページに追加したり、存在するコンテンツの変更やスタイルの変更をします -- ユーザーアクションに反応し、マウスのクリック、ポインタの動き、キーの押下の実行をします -- リモートサーバへネットワーク越しのリクエストを送り、ファイルのダウンロードやアップロードをします( [AJAX](https://en.wikipedia.org/wiki/Ajax_(programming)) や [COMET](https://en.wikipedia.org/wiki/Comet_(programming)) と呼ばれる技術)。 -- クッキーを取得、設定し、訪問者に質問してメッセージを表示します。 -- クライアント側のデータを覚えます("local storage")。 +- 新たなHTMLをページに追加したり、既存のコンテンツの変更やスタイルの変更をする。 +- ユーザーの操作(マウスのクリック、ポインタの動き、キーの押下など)に反応する。 +- リモートサーバへネットワーク越しのリクエストを送り、ファイルのダウンロードやアップロードをする( [AJAX](https://en.wikipedia.org/wiki/Ajax_(programming)) や [COMET](https://en.wikipedia.org/wiki/Comet_(programming)) と呼ばれる技術)。 +- クッキーの取得と設定、訪問者への質問やメッセージを表示する。 +- クライアント側でデータを記憶する("ローカルストレージ")。 ## ブラウザ内のJavaScriptで出来ないことは? -ブラウザでのJavaScriptの機能はユーザの安全のために制限されています。 -その目的は、悪意のあるWebページがプライベートな情報へアクセスしたり、ユーザデータを害するのを防ぐことです。 +ブラウザでは、JavaScriptの機能はユーザの安全のために制限されています。 +その目的は、悪意のあるWebページがプライベートな情報へアクセスしたり、ユーザデータへ危害を加えることを防ぐことです。 -このような制限の例は次のようなものです: +制限の例として、次のようなものがあります: -- Webページ上のJavaScriptは、ハードディスク上の任意のファイルの読み書きや、それらのコピー、プログラムの実行をすることができません。それはOSのシステム機能に直接アクセスすることはできません。 +- Webページ上のJavaScriptは、ハードディスク上の任意のファイルの読み書きや、それらのコピー、プログラムの実行をすることができません。OSのシステム機能に直接アクセスすることはできません。 - 現代のブラウザは、ファイルを扱うことを許可しますが、アクセスは制限されており、ブラウザウィンドウへのファイルの "ドロップ" や、`` タグを経由したファイルの選択と言ったユーザの特定の操作のみ提供しています。 + 現代のブラウザは、ファイルを扱うことはできますがアクセスは制限されており、ブラウザウィンドウへのファイルの "ドロップ" や、`` タグを経由したファイル選択と言ったユーザの特定の操作のみを提供しています。 - カメラ/マイクや他のデバイスとやり取りする方法はありますが、それらはユーザの明示的な許可が求められます。そのため、JavaScriptが有効なページはWebカメラをこそこそと有効にしたり、周囲を観察したり、[NSA](https://en.wikipedia.org/wiki/National_Security_Agency) に情報を送信することはできません。 -- 異なるタブやウィンドウは一般的にお互いについて知りません。時々、例えばあるウィンドウがJavaScriptを利用して別のウィンドウを開くケースはあります。しかし、この場合においても、あるページからのJavaScriptは、別のサイト(異なるドメイン、プロトコル、ポート)からのものである場合は、アクセスすることは出来ません。 + カメラ/マイクやその他デバイスとやり取りする方法はありますが、ユーザの明示的な許可が求められます。したがって、JavaScriptが有効なページがWebカメラを密かに有効にしたり、それを利用して利用者の周囲を観察したり、[NSA](https://en.wikipedia.org/wiki/National_Security_Agency) に情報を送信すると言ったことはできません。 +- 異なるタブやウィンドウは一般的にお互いについて知りません。時々、例えばあるウィンドウがJavaScriptを利用して別のウィンドウを開くケースはあります。しかし、この場合においても、あるページからのJavaScriptは、別のサイト(異なるドメイン、プロトコル、ポート)からのものである場合は、アクセスすることはできません。 これは "Same Origin Policy" と呼ばれています。回避するためには、 *両方のページ* にデータ交換を行うための特別なJavaScriptコードを含める必要があります。 - その制限は、改めてユーザの安全のためです。ユーザが開いた `http://anysite.com` というサイトのページは、URL `http://gmail.com` の別のブラウザのタブにアクセスし、そこから情報を盗むことはできません。 -- JavaScriptはネット越しに、現在のページがきたサーバと簡単にコミュニケーションできます。しかし、他のサイト/ドメインからデータを受信するための機能は犠牲になっています。可能だけれども、リモート側からの明示的な同意(HTTPヘッダに表現)が必要になります。もう一度繰り返しますが、それは安全上の制限です。 + この制限もユーザの安全のためです。ユーザが開いた `http://anysite.com` というサイトのページは、URL `http://gmail.com` の別のブラウザのタブにアクセスし、そこから情報を盗むことはできません。 +- JavaScriptはネットワークを介して、現在のページがきたサーバと簡単にやり取りすることができます。しかし、他のサイト/ドメインからデータを受信することは制限されています。可能ですが、リモート側からの明示的な同意(HTTPヘッダで表現)が必要になります。繰り返しますが、これらは安全上の制限です。 -![](limitations.png) +![](limitations.svg) -もしもJavaScriptがブラウザの外で使われる場合は、このような制限は存在しません。たとえば、サーバ上です。また、現代のブラウザは拡張パーミッションを取得するプラグイン/拡張のインストールを許可しています。 +JavaScriptがブラウザ外で使われる場合はこのような制限は存在しません。たとえば、サーバ上です。また、現代のブラウザは、より拡張されたアクセス権限を必要とするプラグイン/拡張機能を利用することもできます。 ## なにがJavaScriptを特別なものにしている? JavaScriptには少なくとも *3つ* の素晴らしいことがあります: -```比較 +```compare + HTML/CSSとの完全な統合 + シンプルなことはシンプルに + すべてのメジャーブラウザでサポートされており、デフォルトで有効 ``` -これらの3つの事柄はJavaScriptのみで存在し、他のブラウザ技術は存在しません。 +JavaScriptは、これら3つのことを組み合わせた唯一のブラウザテクノロジーです。 -それがJavaScriptをユニークなものにしています。だからこそ、これはブラウザインタフェースを作成するための最も普及したツールです。 +それがJavaScriptをユニークなものにしています。だからこそ、ブラウザインターフェイスを作成するための最も普及しているツールとなっています。 -新しい技術を学ぶことを計画している間、その全体像を確認することは有益です。それでは、新しい言語やブラウザ機能を含む最新のトレンドに移りましょう。 +とは言え、JavaScript を利用してサーバやモバイルアプリケーションなどを作成することもできます。 +## JavaScriptを "覆う" 言語 -## JavaScriptを "超える" 言語 +JavaScriptの構文は、すべての人のニーズにマッチしている訳ではありません。人によって求める機能は異なります。 -JavaScriptの構文は、全員のニーズにマッチはしていません。人によって求める機能は異なります。 +プロジェクトや要件はそれぞれ異なるため、それは自然なことです。 -プロジェクトや要件はすべての人で異なるため、それは予想されます。 +そのため、最近では新しい言語が数多く登場しています。これらはブラウザで実行する前にJavaScriptに *トランスパイル* (変換)されます。 -そのため、最近では新しい言語が数多く登場しており、それはブラウザで実行する前にJavaScriptに *トランスパイル* (変換)されます。 +最新のツールは非常に高速にトランスパイルでき、透過的です。開発者が別の言語でコードを作成し、それを自動変換することができます。 +これは、そのような言語の例です: -最新のツールは非常に早くトランスパイルでき、透過的です。実際、開発者が別の言語でコードを作成し、それを自動変換することができます。 +- [CoffeeScript](http://coffeescript.org/) はJavaScriptの "シンタックスシュガー"です。より短い構文を導入し、より簡潔でクリアなコードを書くことができます。たいてい、Ruby 開発者は好きです。 +- [TypeScript](http://www.typescriptlang.org/) は "厳密なデータ型指定" の追加に焦点をあてています。それは複雑なシステムの開発とサポートを簡素化するためです。これは Microsoftにより開発されています。 +- [Flow](http://flow.org/) もデータ型定義を追加しますが、その方法は異なります。Facebook により開発されました。 +- [Dart](https://www.dartlang.org/) はブラウザ以外の環境(モバイルアプリのような)で動作する独自のエンジンを持ったスタンドアローンな言語ですが、JavaScript へトランスパイルすることもできます。Googleによって開発されました。 +- [Brython](https://brython.info/) は JavaScript への Python トランスパイラで、JavaScript を使用することなく、純粋な Python でアプリケーションを作成することができます。 -このような言語の例です: - -- [CoffeeScript](http://coffeescript.org/) はJavaScripのための "シンタックスシュガー"です。より短い構文を導入し、より簡潔でクリアなコードを書くことができます。たいてい、Ruby 開発者は好きです。 -- [TypeScript](http://www.typescriptlang.org/) は "厳密なデータ型指定" の追加に集中しています。それは複雑なシステムの開発とサポートを簡素化するためです。これは Microsoftにより開発されています。 -- [Dart](https://www.dartlang.org/) はブラウザ以外の環境(モバイルアプリのような)で動作する独自のエンジンを持ったスタンドアローンな言語です。最初はGoogleによってJavaScriptの代わりとして提供されましたが、今のところ、ブラウザは上述のようにそれらをJavaScriptにトランスパイルする必要があります。 - -他にもあります。もちろん、たとえ私たちが上記のような言語を使う場合でも、我々がしていることを本当に理解するためにJavaScriptを知るべきです。 +他にもあります。もちろん、上記のような言語を利用する予定だとしても、実際に行われていることを本当に理解するためにJavaScriptは知っておくのがよいです。 ## サマリ -- JavaScriptは最初ブラウザ用の言語として作られました。しかし今は同様に他の多くの環境で利用されています。 +- JavaScriptは当初ブラウザ用の言語として作られました。しかし今はその他の多くの環境で利用されています。 - 現時点では、JavaScriptはHTML/CSSと完全に統合し、最も広く採用されたブラウザ言語として、独立した地位にいます。 -- JavaScriptに "トランスパイル" し、特定の機能を提供する多くの言語があります。 JavaScriptをマスターした後で、少なくとも簡単に見てみることをお勧めします。 +- JavaScriptに "トランスパイル" し、特定の機能を提供する多くの言語があります。 JavaScriptをマスターした後、少なくとも簡単にでも目を通しておくことをお勧めします。 diff --git a/1-js/01-getting-started/1-intro/limitations.png b/1-js/01-getting-started/1-intro/limitations.png deleted file mode 100644 index d5d315c5c1..0000000000 Binary files a/1-js/01-getting-started/1-intro/limitations.png and /dev/null differ diff --git a/1-js/01-getting-started/1-intro/limitations.svg b/1-js/01-getting-started/1-intro/limitations.svg new file mode 100644 index 0000000000..76ea43fd7a --- /dev/null +++ b/1-js/01-getting-started/1-intro/limitations.svg @@ -0,0 +1 @@ +https://javascript.info<script> ... </script>https://gmail.comhttps://javascript.info \ No newline at end of file diff --git a/1-js/01-getting-started/1-intro/limitations@2x.png b/1-js/01-getting-started/1-intro/limitations@2x.png deleted file mode 100644 index 01483997da..0000000000 Binary files a/1-js/01-getting-started/1-intro/limitations@2x.png and /dev/null differ diff --git a/1-js/01-getting-started/2-code-editors/article.md b/1-js/01-getting-started/2-code-editors/article.md deleted file mode 100644 index ab33956560..0000000000 --- a/1-js/01-getting-started/2-code-editors/article.md +++ /dev/null @@ -1,62 +0,0 @@ -# コードエディタ - -コードエディタはプログラマが最も時間を費やす場所です。 - -大きく2つのタイプがあります: IDE(統合開発環境)と軽量なエディタです。多くの人々はそれぞれのタイプのツールを1つ選んで使うことが多いです。 - -[cut] - -## IDE - -[IDE](https://en.wikipedia.org/wiki/Integrated_development_environment) (統合開発環境)は通常 "プロジェクト全体" での操作をする多くの機能を持ったパワフルなエディタを意味します。つまり、ただのエディタではなく、本格的な "開発環境" です。 - -IDEはプロジェクト(多くのファイル)をロードし、ファイル間のナビゲーションを可能とし、プロジェクト全体に基づいた自動補完を提供、バージョン管理システム([git](https://git-scm.com/) のような)、テスト環境や他の "プロジェクトレベル" のものと統合します。 - -もしもまだIDEの選択を検討していないなら、次のようなIDEを見てみてください。 - -- IntelliJ editors: フロントエンド開発者のための [WebStorm](http://www.jetbrains.com/webstorm/) と他の言語が必要であれば [PHPStorm (PHP)](http://www.jetbrains.com/phpstorm/), [IDEA (Java)](http://www.jetbrains.com/idea/), [RubyMine (Ruby)](http://www.jetbrains.com/ruby/) など。 -- .NET 開発者の場合、Visual Studio は良いです。またフリー版も利用可能です ([Visual Studio Community](https://www.visualstudio.com/vs/community/)) -- [Aptana](http://www.aptana.com/) や Zend Studio のような Eclipse ベースのプロダクト。 -- [Komodo IDE](http://www.activestate.com/komodo-ide) やその軽量のフリー版 [Komodo Edit](http://www.activestate.com/komodo-edit). -- [Netbeans](http://netbeans.org/). - -上に挙げたすべてのIDEはWindowとMacの環境で利用でき、Visual Studio以外のIDEはLinuxでも利用できます。 - -ほとんどのIDEは有償ですが、トライアル期間を持っています。それらのコストはたいてい資格をもつ開発者の給料と比べわずかなので、あなたにとってベストなものを選んでください。 - -## 軽量なエディタ - -"軽量なエディタ" はIDEほど強力ではありませんが、速くエレガントでシンプルです。 - -主にそれらはファイルをすぐに開いて編集するのに使われます。 - -"軽量なエディタ"と"IDE"の主な違いは、IDEはプロジェクトレベルで動作するので、開始時により多くのデータをロードし、必要とされた場合にはプロジェクトの構造の分析等をします。軽量なエディタは、1つのファイルだけが必要な場合、はるかに速いです。 - -なお、実際には軽量なエディタはディレクトリレベルの構文解析や自動補完を含む多くのプラグインを持っている場合があります。そのため、軽量なエディタとIDEの間に厳密な境界はありません。 - -注目に値するものとしては次のような選択肢があります: - -- [Visual Studio Code](https://code.visualstudio.com/) (クロスプラットフォーム、フリー). -- [Atom](https://atom.io/) (クロスプラットフォーム、フリー). -- [Sublime Text](http://www.sublimetext.com) (クロスプラットフォーム、シェアウェア). -- [Notepad++](https://notepad-plus-plus.org/) (Windows, フリー). -- 使い方を知っている場合は、Vim や Emacs もまたクールです。 - -## 私のお気に入り - -著者の個人的な好みはプロジェクトのためのIDEと、素早く簡単にファイル編集するための軽量エディタ、両方を持つことです。 - -私は使っています: - -- JS には [WebStorm](http://www.jetbrains.com/webstorm/), また、プロジェクトでもう1つ言語があるなら[PHPStorm](http://www.jetbrains.com/phpstorm/) (PHP), [IDEA](http://www.jetbrains.com/idea/) (Java), [RubyMine](http://www.jetbrains.com/ruby/) (Ruby)のような Jetbrains の別のエディタを使っています。使ったことはありませんが、他の言語用のものもあります。 -- 軽量なエディタとしては -- [Sublime Text](http://www.sublimetext.com) もしくは [Atom](https://atom.io/) です. - -もしも何を選べばよいか分からない場合、これらを検討してみてください。 - -## 議論はしません - -上記のリストのエディタは私または私がよい開発者だと思っている友人が喜んで長い間利用しているものです。 - -この広い世界には他にも素晴らしいエディタがあります。ぜひあなたが最も好きなものを選んでください。 - -エディタの選択は、他のツールのようにプロジェクト、習慣や個人の趣向に依存します。 diff --git a/1-js/01-getting-started/2-manuals-specifications/article.md b/1-js/01-getting-started/2-manuals-specifications/article.md new file mode 100644 index 0000000000..794bccbd80 --- /dev/null +++ b/1-js/01-getting-started/2-manuals-specifications/article.md @@ -0,0 +1,41 @@ + +# マニュアルと仕様 + +この本は *チュートリアル* であり、あなたが徐々に言語を学ぶのを助けることを目的としています。そのため、基本が理解できたら別の情報源が必要になってきます。 + +## 仕様 + +[The ECMA-262 仕様](https://www.ecma-international.org/publications/standards/Ecma-262.htm) は、JavaScript に関して最も綿密で、詳細かつ形式化された情報を含んでいます。これが言語を定義しています。 + +しかし、形式張っているので最初は理解するのが難しいです。なので、言語の詳細について最も信頼できる情報源が必要であればここが正解ですが、日々使用するものではありません。 + +新しい仕様のバージョンは毎年リリースされます。これらのリリース間での、最新の仕様ドラフトは にあります。 + +"ほぼ標準" (いわゆる "ステージ 3") を含む、新しい最先端の機能については、 の提案を参照してください。 + +また、ブラウザを対象に開発しているのであれば、チュートリアルの [パート 2](info:browser-environment) で説明している、他の仕様もあります。 + +## マニュアル + +- **MDN (Mozilla) JavaScript リファレンス** は、例やその他の情報を含むマニュアルです。ここは、個々の言語関数やメソッドなどに関する情報の詳細を知るのにとても役立ちます。 + + にあります。日本語版は です。 + + ですが、インターネット検索を使う方がベストなことが多いです。`parseInt` 関数を検索する場合、 のように、クエリーを "MDN [用語]" とするだけです。 + +- **MSDN** – JavaScript (しばしば JScript と呼ばれます)を含む、多くの情報をもつ Microsoft のマニュアルです。Internet Explorer 固有のものが必要であれば、ここがベターです: (日本語版: ) + + また、 "RegExp MSDN" または "RegExp MSDN jscript" といったフレーズでインターネット検索することもできます。 + +## 機能のサポート状況 + +JavaScript は開発中の言語であり、定期的に新機能が追加されます。 + +ブラウザベースや他のエンジン間でのサポート状況は以下で見ることができます: + +- - 機能ごとのサポート状況が表で確認できます。例えば、モダンな暗号化機能をサポートしているエンジンを確認するには、 を参照してください。 +- - 言語の機能と、それらをサポートする/しないエンジンの表です。 + +これらのリソースは、言語の詳細やサポートなどに関する貴重な情報が含まれているため、実際の開発に役立ちます。 + +特定の機能に関する詳細な情報が必要な場合は、それら(またはこのページ)を覚えておいてください。 diff --git a/1-js/01-getting-started/3-code-editors/article.md b/1-js/01-getting-started/3-code-editors/article.md new file mode 100644 index 0000000000..2536271d99 --- /dev/null +++ b/1-js/01-getting-started/3-code-editors/article.md @@ -0,0 +1,46 @@ +# コードエディタ + +コードエディタはプログラマが最も時間を費やす場所です。 + +大きく2つのタイプがあります: IDE(統合開発環境)と軽量なエディタです。多くの人々はそれぞれのタイプのツールを1つ選んで使うことが多いです。 + +## IDE + +[IDE](https://en.wikipedia.org/wiki/Integrated_development_environment) (統合開発環境)は通常 "プロジェクト全体" での操作をする多くの機能を持ったパワフルなエディタを意味します。つまり、名前が示すように単なるエディタではなく、本格的な "開発環境" です。 + +IDEはプロジェクト(多くのファイル)をロードし、ファイル間のナビゲーションを可能とし、プロジェクト全体に基づいた自動補完を提供、バージョン管理システム([git](https://git-scm.com/) のような)、テスト環境や他の "プロジェクトレベル" のものと統合します。 + +もしもまだIDEの選択を検討していないなら、次のようなIDEを見てみてください。 + +- [Visual Studio Code](https://code.visualstudio.com/) (クロスプラットフォーム, フリー). +- [WebStorm](http://www.jetbrains.com/webstorm/) (クロスプラットフォーム, 有料). + +Windows には、"Visual Studio"もあります。"Visual Studio Code" と混同しないでください。"Visual Studio" は、.NET プラットフォームに最適な、有料で強力なWindows専用エディタです。JavaScriptも得意です。無料版[Visual Studio Community](https://www.visualstudio.com/vs/community/)もあります。 + +多くのIDEは有料ですが、トライアル期間を持っています。それらのコストはたいてい資格をもつ開発者の給料と比べわずかなので、あなたにとってベストなものを選んでください。 + +## 軽量なエディタ + +"軽量なエディタ" はIDEほど強力ではありませんが、速くエレガントでシンプルです。 + +主にファイルをすぐに開いて編集するのに使われます。 + +"軽量なエディタ"と"IDE"の主な違いは、IDEはプロジェクトレベルで動作するので、開始時により多くのデータをロードし、必要に応じてプロジェクト構造の分析等をします。軽量なエディタは、単一ファイルだけが必要な場合にははるかに速いでしょう。 + +なお、実際には軽量なエディタもディレクトリレベルの構文解析や自動補完を含む多くのプラグインを持っている場合があります。そのため、軽量なエディタとIDEの間に厳密な境界はありません。 + +注目に値するものとしては次のような選択肢があります: + +- [Atom](https://atom.io/) (クロスプラットフォーム、フリー). +- [Visual Studio Code](https://code.visualstudio.com/) (クロスプラットフォーム、フリー). +- [Sublime Text](http://www.sublimetext.com) (クロスプラットフォーム、シェアウェア). +- [Notepad++](https://notepad-plus-plus.org/) (Windows, フリー). +- [Vim](http://www.vim.org/) や [Emacs](https://www.gnu.org/software/emacs/)も使い方を知っているなら良い選択肢です。 + +## 議論はしません + +上記のリストのエディタは、私または私がよい開発者だと思っている友人が喜んで長い間利用しているものです。 + +この広い世界には他にも素晴らしいエディタがあります。ぜひあなたが最も好きなものを選んでください。 + +エディタの選択は、他のツールのようにプロジェクト、習慣や個人の趣向によります。 diff --git a/1-js/01-getting-started/3-devtools/article.md b/1-js/01-getting-started/3-devtools/article.md deleted file mode 100644 index acf38e6671..0000000000 --- a/1-js/01-getting-started/3-devtools/article.md +++ /dev/null @@ -1,61 +0,0 @@ -# 開発者コンソール - -コードはエラーが起きやすいです。あなたもエラーを作る可能性が高いです... -なんの話をしているかというと、少なくともあなたが [ロボット](https://en.wikipedia.org/wiki/Bender_(Futurama)) ではなく人間であるなら、あなたは *絶対に* 間違いをする、ということです。 - -しかしブラウザでは、ユーザはデフォルトではエラーは見えません。そのため、スクリプトが上手くいかない場合、私たちは何が壊れたのか見ることができず、直すこともできないでしょう。 - -エラーを見てスクリプトに関する多くの役立つ情報を得るために、ブラウザには "開発者ツール" が組み込まれています。 - -ほとんどの開発者は、開発するためにChromeかFirefoxに傾きます。なぜなら、それらのブラウザは最高の開発者ツールを持っているからです。開発者ツールを提供している他のブラウザもまた、特別な機能を持っていますが、たいていChromeかFirefoxの "キャッチアップ" です。なので、ほとんどの人々は "お気に入り" のブラウザを持っており、問題がブラウザ依存の場合は他のブラウザに切り替えます。 - -開発者ツールは、本当に強力で多くの機能があります。開始するために、私たちはどうやってそれらを開き、エラーを見て、JavaScriptコマンドを実行するか学びましょう。 - -[cut] - -## Google Chrome - -[bug.html](bug.html)を開きましょう。 - -このJavaScriptコードにはエラーがあります。訪問者の目からは隠れているので、見るために開発者ツールを開きましょう。 - -`key:F12`もしくは、Macの場合は`key:Cmd+Opt+J`を押します。 - -Consoleタブがデフォルトの状態で開発者ツールが起動するでしょう。 - -次のようになります: - -![chrome](chrome.png) - -開発者ツールの正確な見栄えは、あなたのChromeのバージョンに依存します。それは時々変わりますが似ているはずです。 - -- ここでは赤色のエラーメッセージを見ることが出来ます。このケースでは、Scriptは知らない "lalala" コマンドを含んでいます。 -- 右側にはエラーが発生した行番号とともに、ソース `bug.html:12` へのクリック可能なリンクがあります。 - -エラーメッセージの下に、青の `>` のシンボルがあります。それは "コマンドライン" を意味し、私達はJavaScriptコマンドをタイプし、`key:Enter` を押すことでそれら(複数行を入力するには`key:Shift+Enter`)を実行することができます。 - -今や、私たちはエラーを見ることができ、開発を開始するには十分です。私たちは後で開発者ツールに戻ってきて、チャプター でより詳細なデバッグをカバーします。 - - -## Firefox, Edge やその他 - -ほとんどのブラウザは開発者ツールを開くために `key:F12` を使います。 - -それらのルック&フィールはとても似ています。それらの1つ(Chromeで始める事ができます)の使い方を知ったら、簡単に別のものに切り替えることができるでしょう。 - -## Safari - -Safari (Mac ブラウザ, Windows/Linux ではサポートされていません)はここでは少しだけ特別です。最初に、 "開発者メニュー" を有効にする必要があります。 - -Preferencesを開き、"Advanced" ペインに行きます。一番下にチェックボックスがあります。 - -![safari](safari.png) - -`key:Cmd+Opt+C`でコンソールを切り替えることができます。また、"開発"という名前の新しいトップメニュー項目が表示されていることにも注意してください。 それは多くのコマンドとオプションを持っています。 - -## サマリ - -- 開発者ツールは私達がエラーを見たり、コマンドを実行したり、変数を検査したりほかにもおおくのことを可能にします。 -- それらはWindows下では、ほとんどのブラウザは `key:F12` で開くことができます。Mac用のChromeは `key:Cmd+Opt+J` が必要で、Safariは`key:Cmd+Opt+C`です(最初に有効化が必要) - -これで環境が整いました。次のセクションでは、JavaScriptの説明に入ります。 diff --git a/1-js/01-getting-started/3-devtools/safari.png b/1-js/01-getting-started/3-devtools/safari.png deleted file mode 100644 index 37598a2610..0000000000 Binary files a/1-js/01-getting-started/3-devtools/safari.png and /dev/null differ diff --git a/1-js/01-getting-started/3-devtools/safari@2x.png b/1-js/01-getting-started/3-devtools/safari@2x.png deleted file mode 100644 index c59cebef21..0000000000 Binary files a/1-js/01-getting-started/3-devtools/safari@2x.png and /dev/null differ diff --git a/1-js/01-getting-started/4-devtools/article.md b/1-js/01-getting-started/4-devtools/article.md new file mode 100644 index 0000000000..7e78a2e23a --- /dev/null +++ b/1-js/01-getting-started/4-devtools/article.md @@ -0,0 +1,63 @@ +# 開発者コンソール + +コードにエラーはつきものです。少なくともあなたが [ロボット](https://en.wikipedia.org/wiki/Bender_(Futurama)) ではなく人間であるなら、*絶対に* 間違いをする、ということです。 + +しかしブラウザでは、ユーザはデフォルトではエラーは見えません。そのため、スクリプトが上手くいかない場合、何が悪かったのか見ることができず、直すこともできないでしょう。 + +エラーを確認したりスクリプトに関する多くの役立つ情報を得るために、ブラウザには "開発者ツール" が組み込まれています。 + +開発者の多くは、開発するのに Chrome か Firefox を利用することが多いです。なぜなら、それらのブラウザは最高の開発者ツールを持っているからです。開発者ツールを提供している他のブラウザも特別な機能を持っていますが、たいてい Chrome かFirefox の "キャッチアップ" です。なので、ほとんどの開発者は "お気に入り" のブラウザを持っており、問題がブラウザ依存の場合は他のブラウザに切り替えます。 + +開発者ツールは、本当に強力で多くの機能があります。開発を開始するために、私たちはどうやってそれらを開き、エラーを見て、JavaScriptコマンドを実行するのか学びましょう。 + +## Google Chrome + +[bug.html](bug.html)を開きましょう。 + +このJavaScriptコードにはエラーがあります。通常のサイト訪問者の目からは隠れているので、エラーを確認するために開発者ツールを開きましょう。 + +`key:F12`もしくは、Macの場合は`key:Cmd+Opt+J`を押します。 + +開発者ツールが起動します。デフォルトではConsoleタブが開かれているでしょう。 + +次のようになります: + +![chrome](chrome.png) + +開発者ツールの正確な見栄えは、利用しているChromeのバージョンに依存します。見栄えが変わることもありますが似ているはずです。 + +- ここでは赤色でエラーメッセージを見ることができます。このケースでは、スクリプトに未定義の "lalala" コマンドが含まれています。 +- 右側にはエラーが発生した行番号とともに、ソース `bug.html:12` へのクリック可能なリンクがあります。 + +エラーメッセージの下に、青の `>` の記号があります。それは "コマンドライン" を意味し、JavaScriptコマンドを入力し、`key:Enter` を押すことでそれらを実行することができます。 + +これで私たちはエラーを見ることができるようになったので、開発を開始するには十分です。後ほど開発者ツールに戻り、チャプター でより詳細なデバッグをカバーします。 + +```smart header="複数行の入力" +通常、コンソールに1行コードを入力して `key:Enter` を押すと、コードが実行されます。 + +複数行を入力したい場合は、`key:Shift+Enter` を押します。この方法で長い JavaScript コードも入力できます。 +``` + +## Firefox, Edge やその他 + +ほとんどのブラウザは開発者ツールを開くために `key:F12` を使います。 + +それらのルック&フィールはとても似ています。それらの1つ(Chromeで始めるのが良いでしょう)の使い方を知ったら、簡単に別のものに切り替えることができるでしょう。 + +## Safari + +Safari (Mac ブラウザ, Windows/Linux ではサポートされていません)はここでは少しだけ特別です。最初に、 "開発者メニュー" を有効にする必要があります。 + +Preferencesを開き、"Advanced" ペインに行きます。一番下にチェックボックスがあります。 + +![safari](safari.png) + +`key:Cmd+Opt+C`でコンソールを切り替えることができます。また、"開発"という名前の新しいトップメニュー項目が表示されていることにも留意してください。 それは多くのコマンドとオプションを持っています。 + +## サマリ + +- 開発者ツールは私達がエラーを見たり、コマンドを実行したり、変数を検査したりほかにも多くのことを可能にします。 +- Windows下では、ほとんどのブラウザは `key:F12` で開くことができます。Mac用のChromeは `key:Cmd+Opt+J` が必要で、Safariは`key:Cmd+Opt+C`です(最初に有効化が必要)。 + +これで環境が整いました。次のセクションでは、JavaScriptの説明に入ります。 diff --git a/1-js/01-getting-started/3-devtools/bug.html b/1-js/01-getting-started/4-devtools/bug.html similarity index 100% rename from 1-js/01-getting-started/3-devtools/bug.html rename to 1-js/01-getting-started/4-devtools/bug.html diff --git a/1-js/01-getting-started/3-devtools/chrome.png b/1-js/01-getting-started/4-devtools/chrome.png similarity index 97% rename from 1-js/01-getting-started/3-devtools/chrome.png rename to 1-js/01-getting-started/4-devtools/chrome.png index 0aa6b50e07..4cb3ea2f46 100644 Binary files a/1-js/01-getting-started/3-devtools/chrome.png and b/1-js/01-getting-started/4-devtools/chrome.png differ diff --git a/1-js/01-getting-started/3-devtools/chrome@2x.png b/1-js/01-getting-started/4-devtools/chrome@2x.png similarity index 100% rename from 1-js/01-getting-started/3-devtools/chrome@2x.png rename to 1-js/01-getting-started/4-devtools/chrome@2x.png diff --git a/1-js/01-getting-started/4-devtools/safari.png b/1-js/01-getting-started/4-devtools/safari.png new file mode 100644 index 0000000000..64c7a3f6ca Binary files /dev/null and b/1-js/01-getting-started/4-devtools/safari.png differ diff --git a/1-js/01-getting-started/4-devtools/safari@2x.png b/1-js/01-getting-started/4-devtools/safari@2x.png new file mode 100644 index 0000000000..27def4d09b Binary files /dev/null and b/1-js/01-getting-started/4-devtools/safari@2x.png differ diff --git a/1-js/02-first-steps/01-hello-world/1-hello-alert/index.html b/1-js/02-first-steps/01-hello-world/1-hello-alert/index.html new file mode 100644 index 0000000000..ff1d871b08 --- /dev/null +++ b/1-js/02-first-steps/01-hello-world/1-hello-alert/index.html @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/1-js/02-first-steps/01-hello-world/article.md b/1-js/02-first-steps/01-hello-world/article.md index ff45f21312..7c49ebbf69 100644 --- a/1-js/02-first-steps/01-hello-world/article.md +++ b/1-js/02-first-steps/01-hello-world/article.md @@ -1,17 +1,15 @@ # Hello, world! -あなたの読んでいるチュートリアルは、プラットフォームに依存しないJavaScriptのコアについてです。さらに、Node.JSとそれを使用する他のプラットフォームについて学びます。 +このチュートリアルは、プラットフォームに依存しないJavaScriptのコアについてです。もっと先に、Node.jsや他のプラットフォームについて学びます。 -しかし、私たちはスクリプトを動かすための動作環境が必要です。ちょうどこの本はオンラインなのでブラウザは良い選択肢です。ここでは、ブラウザ固有のコマンド(`alert`のような)を最小限になるようにします。あなたが Node.JS のような別の環境に集中する予定の場合、それに時間を費やさないようにするために。一方、ブラウザの詳細はチュートリアルの[次のパート](/ui) で詳しく説明します。 +しかし、私たちは今スクリプトを動かすための動作環境が必要です。ちょうどこのチュートリアルはオンラインなので、ブラウザが良い選択肢です。もしも別の環境( Node.js など)に集中する予定がある場合に時間を費やさないように、ここでは、ブラウザ固有のコマンド(`alert`のような)が最小限になるようにします。チュートリアルの[次のパート](/ui) では、ブラウザの JavaScript に焦点を当てます。 -では最初に、どうやってWebページにScriptを付け加えるか見てみましょう。サーバサイドでは、Node.JSの場合の `"node my.js"` のようにコマンドと一緒に実行するだけです。 +では最初に、どうやってWebページにScriptを付け加えるか見てみましょう。サーバサイドでは、Node.jsの場合 `"node my.js"` のようにコマンドと一緒に実行するだけです。 -[cut] - ## "script" タグ -JavaScriptプログラムは、` ``` - それらのコメントは` ``` -ここで `/path/to/script.js` はスクリプトファイルの絶対パスです(サイトルートからの) - -また、現在のページからの相対パスを提供することもできます。例えば、`src="script.js"`は現在のフォルダの`"script.js"`を意味するでしょう。 +ここで `/path/to/script.js` はスクリプトファイルの絶対パスです(サイトルートからの)。また、現在のページからの相対パスで指定することもできます。例えば、`src="script.js"` は現在のフォルダにある `"script.js"` を意味するでしょう。 完全なURLも同様に可能です。たとえば: @@ -86,7 +81,7 @@ Scriptの前後のコメント ``` -いくつかのスクリプトを使う場合は、複数のタグを使います: +複数のスクリプトを使う場合は、複数のタグを使います: ```html @@ -95,13 +90,13 @@ Scriptの前後のコメント ``` ```smart -原則として、最もシンプルなスクリプトだけがHTML内に置かれます。より複雑なものは別のファイルに置きます。 +原則として、最もシンプルなスクリプトだけをHTML内に置きます。より複雑なものは別のファイルに置きます。 ファイル分割のメリットは、ブラウザがダウンロードしてそれをブラウザの[キャッシュ](https://en.wikipedia.org/wiki/Web_cache)に保存するためです。 -この後、同じスクリプトが必要な他のページはダウンロードする代わりにキャッシュからそれを取得します。そのため、ファイルは実際には一度だけダウンロードされます。 +以降、同じスクリプトが必要な他のページは、ダウンロードする代わりにキャッシュから取得します。そのため、ファイルは実際には一度だけダウンロードされます。 -それはトラフィックを節約しページをより速くします。 +これはネットワーク通信量を節約しページをより速くします。 ``` ````warn header="もし `src` が設定された場合, スクリプトの中身は無視されます。" @@ -115,7 +110,7 @@ Scriptの前後のコメント ``` -選ぶ必要があります: 外部の `` で挿入されます。 +- 外部ファイルのスクリプトは、`` で挿入できます。 -ブラウザスクリプトやそれらとWebページとのやり取りについては、学ぶことがまだまだあります。しかし、このチュートリアルのこのパートは JavaScript 言語に専念していることに留意してください。JavaScriptを実行するための方法としてここではブラウザを使っており、それはオンラインでの読み込みではとても便利ですが、数ある実行方法の1つです。 +ブラウザスクリプトやそれらとWebページとのやり取りについては、学ぶことがまだまだあります。しかし、このチュートリアルのこのパートは JavaScript 言語に専念していることに留意してください。JavaScriptを実行するための方法としてここではブラウザを使っており、オンラインでの読み込みではとても便利ですが、数ある実行方法の1つでしかありません。 diff --git a/1-js/02-first-steps/01-hello-world/hello-world-render.png b/1-js/02-first-steps/01-hello-world/hello-world-render.png deleted file mode 100644 index ffe810697c..0000000000 Binary files a/1-js/02-first-steps/01-hello-world/hello-world-render.png and /dev/null differ diff --git a/1-js/02-first-steps/01-hello-world/hello-world-render@2x.png b/1-js/02-first-steps/01-hello-world/hello-world-render@2x.png deleted file mode 100644 index c4411027ca..0000000000 Binary files a/1-js/02-first-steps/01-hello-world/hello-world-render@2x.png and /dev/null differ diff --git a/1-js/02-first-steps/02-structure/article.md b/1-js/02-first-steps/02-structure/article.md index bb605d2dce..66e8da1bec 100644 --- a/1-js/02-first-steps/02-structure/article.md +++ b/1-js/02-first-steps/02-structure/article.md @@ -1,24 +1,22 @@ # コード構造 -最初の勉強は、コードのビルディングブロックです。 - -[cut] +最初に学ぶことは、コードの基本的な構成要素です。 ## 文(命令文) 文は、構文構造でアクションを実行するコマンドです。 -私たちは既に `alert('Hello, world!')` という文を見ました。それはメッセージを表示します。 +私たちはすでに `alert('Hello, world!')` という文を見ており、これは "Hello, world!" というメッセージを表示します。 -私たちは、望むだけの多くの文をコードに含めることができます。文はセミコロンで区切ることができます。 +コードには必要なだけ文を含めることができ、文はセミコロンで区切ることができます。 -たとえば、ここではメッセージを2つに分けます: +たとえば、これは "Hello World" のメッセージを2つのアラートに分けます: ```js run no-beautify alert('Hello'); alert('World'); ``` -通常、各文は別の行に書かれます -- これによりコードはより読みやすくなります。 +通常、それぞれの文は別の行に書かれます。これによりコードの可読性が向上します。 ```js run no-beautify alert('Hello'); @@ -27,19 +25,19 @@ alert('World'); ## セミコロン -セミコロンは改行が存在するとき、ほとんどのケースで省略されます。 +改行があるほとんどの場合、セミコロンは省略することが可能です。 -これもまた動作します: +これも動作します: ```js run no-beautify alert('Hello') alert('World') ``` -ここでJavaScriptは、改行を "暗黙" のセミコロンとして解釈します。 -それはまた[自動セミコロン挿入](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion)と呼ばれます。 +ここで JavaScript は、改行を "暗黙" のセミコロンと解釈します。 +これは[自動セミコロン挿入](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion)と呼ばれます。 -**ほとんどのケースで改行はセミコロンを意味します。しかし "ほんどのケース" は "いつも" ではありません!** +**ほとんどのケースで改行はセミコロンを意味します。しかし "ほとんどのケース" は "常に" ではありません!** これは改行はセミコロンを意味しないケースです、例えば: @@ -49,20 +47,20 @@ alert(3 + + 2); ``` -このコードは `6` を出力します、なぜなら JavaScript はここではセミコロンを挿入しないからです。もし行の終わりがプラス `"+"` で終わっている場合、直感的には "不完全な表現" であり、セミコロンが必要ないのは明らかです。そしてこのケースでは、それは意図したとおりに動作します。 +このコードは `6` を出力します、なぜなら JavaScript はセミコロンを挿入しないからです。もし行の終わりがプラス `"+"` で終わっている場合、直感的には "不完全な表現" であり、セミコロンが必要ないのは明らかです。このケースでは、それは意図した通りに動作します。 -**しかしJavaScriptが、本当に必要なときにセミコロンを想定 "し損なう" ケースがあります。** +**しかし本当に必要なときに JavaScriptがセミコロンを想定 "し損なう" ケースがあります。** -このようなケースで発生するエラーは検出と修正が非常に難しいです。 +このようなケースで発生するエラーは検出と修正が非常に困難です。 ````smart header="エラーの例" -もしもあなたがこのようなエラーの具体例を見て興味があるなら、このコードを確認してください: +このようなエラーの具体例に興味があるなら、このコードを確認してみてください: ```js run [1, 2].forEach(alert) ``` -角括弧 `[]` や `forEach` の意味についてはまだ考える必要はありません。私達はあとでそれらを勉強するでしょう、今のところそれは問題ではありません。ただ結果を覚えておきましょう: "1", そして "2" が表示されます。 +角括弧 `[]` や `forEach` の意味についてはまだ考える必要はありません。それらについては後ほど勉強するので今のところ問題ではありません。ただ結果を覚えておきましょう: "1", そして "2" が表示されます。 今、コードの前に `alert` を追加し、セミコロンで終わら "ない" ようにしましょう: @@ -72,20 +70,18 @@ alert("There will be an error") [1, 2].forEach(alert) ``` -それを実行した場合、最初の `alert` だけが表示され、エラーが発生するでしょう! +実行すると、最初の `alert` だけが表示され、エラーが発生するでしょう! -しかし、もしも `alert` の後にセミコロンをつけた場合はすべてうまく行きます: +しかし、`alert` の後にセミコロンをつけた場合はすべてうまく行きます: ```js run alert("All fine now"); [1, 2].forEach(alert) ``` -今や私たちは "All fine now" メッセージ、そして `1`, `2` を得ます。 - - -セミコロンのない場合のエラーは、JavaScript は角括弧 `[...]` の前にはセミコロンを暗示しないために発生します。 +これで "All fine now" メッセージ、そして `1`, `2` が表示されます。 +JavaScriptでは角括弧 `[...]` の前にはセミコロンを想定しません。 そのため、セミコロンは自動挿入されないので最初の例のコードは1つの文として扱われます。 エンジンは次のように解釈しています: @@ -93,22 +89,22 @@ alert("All fine now"); alert("There will be an error")[1, 2].forEach(alert) ``` -しかし、それは1つではなく2つの別の文であるべきです。今回のケースのようなマージは間違っているのでエラーです。このようなことが起こるシチュエーションは他にもあります。 +しかし、本来は1つではなく2つの文であるべきです。今回のケースのようなマージは間違っているのでエラーです。このようなことが起こる状況は他にもあります。 ```` -改行によって文が分割されていたとしてもセミコロンを置くことを推奨します。このルールはコミュニティで広く採用されています。再度留意しましょう -- ほとんどの場合でセミコロンは除くことが *可能* です。しかし、特に初心者はセミコロンを使うのが安全です。 +改行によって文が分割されるとしてもセミコロンを置くことを推奨します。このルールはコミュニティで広く採用されています。改めて留意しましょう -- ほとんどの場合、セミコロンは除くことが *可能* です。しかし、特に初心者はセミコロンを使う方が安全です。 ## コメント -時が経つにつれて、プログラムはどんどん複雑になります。起こることと、なぜを説明するための *コメント* を追加し始める必要があります。 +時が経つにつれて、プログラムはどんどん複雑になります。起こることと、なぜを説明するために *コメント* を追加し始める必要があります。 -コメントはスクリプトのどの場所にも書くことができます。エンジンは単にそれらを無視するので、実行には影響しません。 +コメントはスクリプトのどの場所にも書くことができます。エンジンはそれらを無視するので、実行には影響しません。 **1行のコメントは、2つのスラッシュ文字 `//` から始まります。** 残りの行はコメントです。それは行全体または文に従います。 -ここのように: +このようになります: ```js run // この行全体がコメントになります。 alert('Hello'); @@ -118,7 +114,7 @@ alert('World'); // このコメントは文の後に続いています。 **複数行のコメントはスラッシュとアスタリスク /* で始まり、アスタリスクとスラッシュ */ で終わります。** -このように: +このようになります: ```js run /* 2つのメッセージを含む例。 @@ -128,10 +124,10 @@ alert('Hello'); alert('World'); ``` -コメントの内容は無視されます、そのためもしも /* ... */ の中にコードをおいても、それは実行されないでしょう。 +コメントの内容は無視されます、そのため、/* ... */ の中にコードをおいても実行されません。 -時々、コードの一部を一時的に無効にすると便利です: +これはコードの一部を一時的に無効にしたいときに便利です: ```js run /* コードのコメントアウト @@ -148,7 +144,7 @@ Macでは、 `key:Ctrl` の代わりに `key:Cmd` を試してください。 ````warn header="入れ子のコメントはサポートされていません!" 別の `/*...*/` の中に `/*...*/` はありません。。 -このようなコードはエラーで死にます。 +このようなコードはエラーになります。 ```js run no-beautify /* @@ -158,8 +154,8 @@ alert( 'World' ); ``` ```` -コードにコメントするのをためらわないでください。 +コードにコメントするのを躊躇わないでください。 -コメントは全体のコード規模を増加させますが、それは全く問題ではありません。プロダクションサーバへリリースする前にコードを minify する多くのツールがあります。それらはコメントを除去するので、実行スクリプトの中には現れません。そのため、コメントはプロダクション上でネガティブな影響は全くありません。 +コメントはコード規模を増加させますが、それは全く問題ではありません。プロダクションサーバへリリースする前にコードを minify する多くのツールがあります。それらはコメントを除去するので、実行されるスクリプトには現れません。そのため、コメント書くことによるネガティブな影響は全くありません。 -さらにこのチュートリアルでは、より良いコメントの書き方を説明するチャプター があります。 +さらにこのチュートリアルでは、より良いコメントの書き方を説明するチャプター もあります。 diff --git a/1-js/02-first-steps/03-strict-mode/article.md b/1-js/02-first-steps/03-strict-mode/article.md index 9c6a49aa18..61e00aab05 100644 --- a/1-js/02-first-steps/03-strict-mode/article.md +++ b/1-js/02-first-steps/03-strict-mode/article.md @@ -1,8 +1,8 @@ # モダンなモード, "use strict" -長い間、JavaScriptは互換性の問題なしに進化していました。新しい機能が言語に追加されましたが、古い機能は変更されませんでした。 +長い間、JavaScriptは互換性の問題なしに進化していました。新しい機能は言語に追加されましたが、古い機能は変更されませんでした。 -それは既存のコードが決して壊れないというメリットがありました。しかし、欠点は、JavaScript作成者による間違いや不十分な決定は、永遠にこの言語から抜け出せなくなったことです。 +それは既存のコードが決して壊れないというメリットがありました。しかし、欠点はJavaScript作成者による間違いや不十分な決定がこの言語から抜け出せなくなったことです。 ECMAScript 5(ES5) が登場したときは2009年でした。新しい機能が言語に追加され、既存の機能のいくつかが修正されました。古いコードが動作するのを保つために、ほとんどの修正はデフォルトではOFFです。特別なディレクティブ `"use strict"` を明示的に有効にする必要があります。 @@ -10,9 +10,9 @@ ECMAScript 5(ES5) が登場したときは2009年でした。新しい機能が ## "use strict" -そのディレクティブは文字列のように見えます: `"use strict"` もしくは `'use strict'`. スクリプトの先頭に位置する場合、すべてのスクリプトは "最新の" 方法で動作します。 +そのディレクティブは文字列のように見えます: `"use strict"` もしくは `'use strict'`。 これがスクリプトの先頭に位置する場合、すべてのスクリプトは "最新の" 方法で動作します。 -たとえば +例えば ```js "use strict"; @@ -21,14 +21,11 @@ ECMAScript 5(ES5) が登場したときは2009年でした。新しい機能が ... ``` -私たちは 関数(コマンドをグループ化する方法) をすぐに学ぶでしょう。 - -先読みの備考として、`"use strict"` は全体のスクリプトの代わりに関数(ほとんどの種類の関数) の最初に置くことができます。 -その場合は、その関数内でのみStrictモードが有効になります。しかし通常はスクリプト全体に対して使います。 - +私たちはこの後まもなく関数(コマンドをグループ化する方法)を学ぶのでここで言及しますが、`"use strict"` は関数の頭に置くことができることに留意してください。 +この場合はその関数内でのみStrictモードが有効になります。しかし通常はスクリプト全体に対して使います。 ````warn header="\"use strict\" が先頭にあることを保証してください" -`"use strict"` がスクリプトの先頭かを確認してください、そうでなければ strict mode は有効でないかもしれません。 +`"use strict"` がスクリプトの先頭にあるようにしてください。そうでない場合 strict mode が有効でないかもしれません。 これは strict mode ではありません: @@ -41,7 +38,7 @@ alert("some code"); // strict mode はアクティブになりません ``` -コメントは `"use strict"` の上に現れることがあります。 +コメントだけは `"use strict"` の上に置けます。 ```` ```warn header="`use strict` をキャンセルする方法はありません" @@ -50,15 +47,47 @@ alert("some code"); 一度 strict mode に入ったら戻ることはありません。 ``` -## 常に "use strict" +## ブラウザコンソール + +実行コードに対して[開発者コンソール](info:devtools)を使用する場合、デフォルトでは `use strict` ではないことに留意してください。 + +`use strict` が差異を生むと、正しくない結果が得られる可能性があります。 + +ではどうやってコンソールで `use strict` を使うのでしょうか。 + +最初に、複数行入力するために `key:Shift+Enter` を押して、次のように `use strict` を置きます。 + +```js +'use strict'; +// ...コードを書く + +``` + +多くのブラウザ、すなわち Firefox や Chrome では動作します。 + + +もし古いブラウザなどでそれができない場合、いまいちではありますが `use strict` を確実にするための信頼できる方法があります。ラッパーの中に置きます: + +```js +(function() { + 'use strict'; + + // ...your code here... +})() +``` + +## "use strict" は必要? + +この質問は明白かもしれませんが、そうではありません。 + +`"use strict"` でスクリプトを開始することをオススメします...が何がよいか知っているでしょうか? + +モダンな JavaScript は "クラス" や "モジュール" --- 高度な言語構造をサポートしており、これらは `use strict` を自動的に有効にします。したがって、それらを利用する場合、`"use strict"` を追加する必要はありません。 -`"use strict"` と "default" モードの違いはまだ説明されています。 +**なので、今のところ `"use strict";` はスクリプトの先頭に書くことを推奨します。後でコードがすべてクラスとモジュールに含まれている場合は省略できます。** -次のチャプターでは、言語の機能を学ぶ際に strict mode の違いについて書き留めます。幸いにもそれは多くはありません。そしてそれらは実際に我々の開発をより良くします。 +現段階では、`use strict` について一般的なことを知っていれば十分です -現段階では、一般的にそれについて知るだけで十分です: +次のチャプターでは、言語の機能を学びながら strict モードと 古いモードの違いについて説明します。幸い、それほど多くありません。そしてそれらは実際に我々の開発をより良くします。 -1. `"use strict"` ディレクティブは "最新" モードにエンジンを切り替え、いくつかの組み込みの機能の振る舞いを変更します。勉強しながらその詳細を見ていきましょう。 -2. strict mode は先頭の `"use strict"` で有効になります。また、自動的に strict mode を有効にする "classes" や "modules" のようないくつかの機能もあります。 -3. strict mode はすべてのモダンブラウザによってサポートされています。 -4. `"use strict"` で始まるスクリプトはいつでも推奨されます。このチュートリアルのすべての例は、そうでないと明示されていない限り(ほとんどないですが)それを想定しています。 +常に `"use strict"` で始まるスクリプトは推奨されます。このチュートリアルのすべての例は、そうでないと明示されていない限り(ほとんどないですが)それを想定しています。 diff --git a/1-js/02-first-steps/04-variables/article.md b/1-js/02-first-steps/04-variables/article.md index b8b1ff322d..39b8cebac0 100644 --- a/1-js/02-first-steps/04-variables/article.md +++ b/1-js/02-first-steps/04-variables/article.md @@ -1,18 +1,16 @@ # 変数 -ほとんどの場合で、JavaScript アプリケーションは情報を処理する必要があります。ここに2つの例があります。 -1. オンラインショップ -- 情報は売られている商品やショッピングカートを含むかもしれません。 -2. チャットアプリケーション -- 情報はユーザ、メッセージやその他より多くのものが含まれるかもしれません。 +ほとんどの場合、JavaScript アプリケーションは情報を処理する必要があります。ここに2つの例があります。 +1. オンラインショップ -- 情報は "売られている商品" や "ショッピングカート" などが考えられます。 +2. チャットアプリケーション -- 情報は "ユーザ"、"メッセージ" やその他より多くのものが考えられます。 変数は情報を保持するために使われます。 -[cut] - ## 変数 -[変数](https://en.wikipedia.org/wiki/Variable_(computer_science)) はデータのための "名前付けされた格納場所" です。私たちは商品や訪問者、その他のデータを格納するために変数を使うことができます。 +[変数](https://en.wikipedia.org/wiki/Variable_(computer_science)) はデータのための "名前付けされた格納場所" です。私たちは商品や訪問者、その他のデータを格納するために変数が利用できます。 -JavaScriptで変数を作るために、`let` キーワードを使います。 +JavaScriptで変数を作るには、`let` キーワードを使います。 下の文は "message" という名前の変数を作ります(別の言い方: *宣言* または *定義* ): @@ -20,7 +18,7 @@ JavaScriptで変数を作るために、`let` キーワードを使います。 let message; ``` -代入演算子 `=` を使って、そこにデータを置く事ができます。 +代入演算子 `=` を使って、データを置く事ができます。 ```js let message; @@ -30,7 +28,7 @@ message = 'Hello'; // 文字列を格納します */!* ``` -文字列は今、変数に関連付けられたメモリ領域に保存されています。変数名を使ってそれにアクセスすることができます。: +今、文字列は変数に関連付けられたメモリ領域に保存されています。変数名を使ってアクセスすることができます。: ```js run let message; @@ -41,7 +39,7 @@ alert(message); // 変数の中身を表示します */!* ``` -簡潔にするために、変数宣言と代入を1行にマージすることができます。 +簡潔にするために、変数宣言と代入を1行で書くことができます。 ```js run let message = 'Hello!'; // 変数宣言と値の代入 @@ -57,7 +55,7 @@ let user = 'John', age = 25, message = 'Hello'; より短いように見えるかもしれませんが、これは推奨しません。可読性のため、1変数1行にしてください。 -複数行のバリアントはちょっと長いですが、読みやすいです。 +複数行のケースはちょっと長くなりますが、読みやすいです。 ```js let user = 'John'; @@ -65,7 +63,7 @@ let age = 25; let message = 'Hello'; ``` -多くの変数をこのように書く人もいます: +多くの変数がある場合、このように書く人もいます: ```js no-beautify let user = 'John', age = 25, @@ -80,11 +78,10 @@ let user = 'John' , message = 'Hello'; ``` -技術的には、これらすべてのバリアントは同じです。なので、これは個人の好みと美学の問題です。 - +技術的にはこれらすべてのパターンは同じです。なので、これは個人の好みと美学の問題です。 ````smart header="`let` の代わりに `var`" -古いスクリプトでは、別のキーワードを見つけるかもしれません: `let` の代わりに `var`: +古いスクリプトには、別のキーワードがあるかもしれません: `let` の代わりに `var` です: ```js *!*var*/!* message = 'Hello'; @@ -97,16 +94,15 @@ let user = 'John' ## 現実での例え -ユニークな名前のステッカーを付けて、データの "ボックス" として想像すれば、私たちは "変数" という概念を簡単に把握できます。 - -たとえば、変数 `message` は値 `"Hello!"` を持った `"message"` とラベル付けされたボックスとして想像することができます。: +ユニークな名前のステッカーを付けたデータの "箱" として想像すれば、"変数" という概念を簡単に把握できます。 -![](variable.png) +たとえば、変数 `message` は値 `"Hello!"` を持った `"message"` とラベル付けされた箱として想像することができます。: -私たちはボックスの中にどんな値でも入れることができます。 +![](variable.svg) -また、それを変えることもできます。値は必要な分だけ何度でも変更することができます。 +箱の中にはどんな値でも入れることができます。 +また、それを変えることもできます。値は必要なだけ何度でも変更することができます。 ```js run let message; @@ -119,7 +115,7 @@ alert(message); 値が変更されたとき、古い値は変数から削除されます。 -![](variable-change.png) +![](variable-change.svg) 私たちは2つの変数を宣言して、ある変数から別の変数にデータをコピーすることもできます。 @@ -138,12 +134,26 @@ alert(hello); // Hello world! alert(message); // Hello world! ``` -```smart header="関数言語" -変数の値の変更を禁止する[functional](https://en.wikipedia.org/wiki/Functional_programming)プログラミング言語も存在することは興味深いかもしれません。たとえば, [Scala](http://www.scala-lang.org/) または [Erlang](http://www.erlang.org/). +````warn header="2回宣言すると、エラーが発生します" +変数は一度だけ宣言する必要があります。 -このような言語では、一度 "ボックスの中に" 値を格納すると、それは永遠です。もし他の何かを格納する必要がある場合、その言語は新しいボックスを作ることを強制します(新しい変数を宣言します)。私達は古いものを再利用することはできません。 +同じ変数の繰り替えし宣言はエラーになります: -一見すると少し奇妙に見えるかもしれませんが、それらの言語は本格的な開発が可能です。それ以上に、この制限が確かな利益を与える並列計算のような領域があります。このような言語を勉強することは、見識を広げるために推奨されます(たとえすぐにそれを使う予定がなくても)。 +```js run +let message = "This"; + +// 'let' の繰り返しはエラーになります +let message = "That"; // SyntaxError: 'message' has already been declared +``` +そのため、変数は1度だけ宣言し、その後は `let` なしで参照する必要があります。 +```` + +```smart header="関数型言語" +変数の値の変更を禁止する[関数型プログラミング言語](https://ja.wikipedia.org/wiki/関数型言語)と呼ばれる言語も存在することを知っておくと良いかもしれません。関数型言語の例として[Scala](http://www.scala-lang.org/) や[Erlang](http://www.erlang.org/)が挙げられます。 + +このような言語では、一度"箱の中"に格納された値は、永遠に変化することがありません。もし他の値を"箱の中"に格納したい場合、新たな箱を作る、すなわち新たな変数を宣言する必要があります。一度使った変数を再利用することはできません。 + +一見すると少し奇妙に見えるかもしれませんが、それらの言語は本格的な開発に適しています。それ以上に、並列計算のような分野ではこの制限が恩恵をもたらしさえします。このような言語を勉強することは視野を広げるために推奨されています(たとえすぐにそれを使う予定がなくても)。 ``` ## 変数のネーミング @@ -160,11 +170,11 @@ let userName; let test123; ``` -名前に複数の単語を含む場合、[camelCase](https://en.wikipedia.org/wiki/CamelCase) が一般的に使われます。 つまり: 単語が続々と続き, 各単語は大文字で始まります。: `myVeryLongName`. +名前に複数の単語を含む場合、[camelCase](https://en.wikipedia.org/wiki/CamelCase) が一般的に使われます。 つまり、単語が続々と続き、各単語は大文字で始まります: `myVeryLongName`. -興味深いことに -- ドル `'$'` や アンダースコア `'_'` も名前に使われます。それらは特別な意味をもたない、ただの文字のような正規の記号です。 +興味深いことに -- ドル `'$'` や アンダースコア `'_'` も名前に使うことができます。それらは単に文字のように特別な意味をもたない普通の記号です。 -それらの名前は正しいです: +これらは有効な名前です: ```js run untrusted let $ = 1; // 名前 "$" の変数を宣言 @@ -193,11 +203,11 @@ let имя = '...'; let 我 = '...'; ``` -技術的には、ここでエラーは起きず、このような名前は許可されます。しかし、変数名では英語を使うのが国際的な伝統です。たとえ小さなスクリプトを書いているとしても、そのコードはずっと残るかもしれません。他の国の人々がそれを見ることがあるかもしれません。 +技術的には、ここでエラーは起きず、このような名前も許可されます。しかし変数名では英語を使うのが国際的な伝統です。たとえ小さなスクリプトを書いているとしても、そのコードはこの先も残るかもしれません。他の国の人々がそれを見ることがあるかもしれません。 ```` ````warn header="予約された名前" -予約語の一覧があり、それらは変数としては使用することができません。なぜならそれらは言語自身によって使われるからです。 +[予約語の一覧](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords)があります。これらは言語自身によって使用されるため、変数名として使用することはできません。 たとえば、単語 `let`, `class`, `return`, `function` は予約されています。 @@ -211,7 +221,7 @@ let return = 5; // 同様に "return" という名前もエラーです! ````warn header="`use strict` なしでの代入" -通常、変数を使う前に定義する必要があります。しかし、以前は `let` なしで、単に値を代入することによって変数を作成することが技術的に可能でした。`use strict` でない場合はこれは今でも動作します。その振る舞いは古いスクリプトの互換性のために維持されています。 +通常、変数を使う前に定義する必要があります。しかし、以前は `let` なしで、単に値を代入するだけで変数を作成することが技術的に可能でした。`use strict` でない場合には今でも動作します。この動作は古いスクリプトの互換性のために維持されています。 ```js run no-strict // 注意: この例は "use strict" なしモードです @@ -221,7 +231,7 @@ num = 5; // 存在しなかった場合、変数 "num" が作られます alert(num); // 5 ``` -これは悪い悪い習慣です、strict モードではエラーになります: +これは悪い習慣です、strict モードではエラーになります: ```js run untrusted "use strict"; @@ -230,7 +240,6 @@ alert(num); // 5 num = 5; // エラー: num が未定義です */!* ``` - ```` ## 定数 @@ -241,7 +250,7 @@ num = 5; // エラー: num が未定義です const myBirthday = '18.04.1982'; ``` -`const` を使って宣言された変数は "定数" と呼ばれます。それらは変更することが出来ません。その試みはエラーを引き起こします。: +`const` を使って宣言された変数は "定数" と呼ばれます。それらは変更することが出来ません。変更しようとするとエラーになります: ```js run const myBirthday = '18.04.1982'; @@ -249,16 +258,16 @@ const myBirthday = '18.04.1982'; myBirthday = '01.01.2001'; // エラー, 定数の再代入はできません! ``` -プログラマがその変数は決して変更されないべきと確信するときそれを保証し、またみんなにその事実を明示的に示すために `const` を使うことが出来ます。 +プログラマがその変数は決して変更されるべきでないと確信するとき、それを保証しつつみんなにその事実を明示的に示すために `const` を使います。 ### 大文字の定数 -実行する前にわかっている覚えるのが難しい値のために、エイリアスとして定数を使うという慣習は広く行われています。 +実行する前に分かっているが、覚えるのが難しいという値に対しては、エイリアスとして定数を使うという慣習は広く行われています。 このような定数は大文字とアンダースコアを使って名前がつけられます。 -このように: +例えば、いわゆる "web"(16進数) 形式での色の定数を作りましょう: ```js run const COLOR_RED = "#F00"; @@ -274,21 +283,21 @@ alert(color); // #FF7F00 メリット: - `COLOR_ORANGE` は `"#FF7F00"` よりも覚えるのが遥かに簡単です。 -- `COLOR_ORANGE` よりも `"#FF7F00"` のほうがミスタイプをし易いです。 +- `COLOR_ORANGE` よりも `"#FF7F00"` のほうがタイプミスをし易いです。 - コードを読むとき、`#FF7F00` よりも `COLOR_ORANGE` のほうがより意味があります。 -いつ定数に大文字を使うべきなのか、通常はいつそれらの命名をするべきなのか?それらを明らかにしましょう。 +いつ定数に大文字を使うべきなのか、いつ通常の名前をつけるべきなのかを明らかにしましょう。 -"定数" であることは、その値は決して変わらないことを意味します。しかし、実行する前に知られている定数(赤の16進数のような)があります。また、実行中にランタイムで *計算* されますが、代入後は変更されないものもあります。 +"定数" であることは、その値は決して変わらないことを意味します。が、実行する前に知られている定数(赤の16進数のような)と、実行中にランタイムで *計算* されますが、代入後は変更されないものがあります。 たとえば: ```js const pageLoadTime = /* webページの読み込み時間 */; ``` -`pageLoadTime` の値はページロードする前にはわからないので、通常の名前がつけられます。しかし代入後に変更されないので、それは定数です。 +`pageLoadTime` の値はページロードする前にはわからないので、通常の名前がつけられます。しかし代入後は変更されないのでこれも定数です。 -言い換えると、大文字の名前の定数は "ハードコードされた" 値のエイリアスとしてのみ使われます。 +つまり、大文字の名前の定数は "ハードコードされた" 値のエイリアスとしてのみ使います。 ## 正しい名前をつける @@ -298,37 +307,37 @@ const pageLoadTime = /* webページの読み込み時間 */; 変数のネーミングは、プログラミングにおいて、もっとも重要で複雑なスキルの1つです。変数名をちょっと見れば、どのコードが初心者で書かれ、どれが経験豊富な開発者によって書かれたものかがわかります。 -実際のプロジェクトでは、スクラッチで完全に分離された何かを書くよりも、既存のコードベースの修正や拡張に最も時間を費やします。そして、何か他のことをした後にコードに戻ったとき、よくラベル付けされた情報を探すのははるかに簡単です。それは、言い換えると、適切な名前がついている変数、です。 +実際のプロジェクトでは、スクラッチで完全に分離された何かを書くよりも、既存のコードベースの修正や拡張に最も時間を費やします。そして、何か他のことをした後にコードに戻ったとき、よくラベル付けされた情報を探すのははるかに簡単です。それは言い換えると、適切な名前がついている変数、です。 変数を宣言する前に正しい名前について考えるようにしてください。それは多くのことに報いるでしょう。 いくつかの良いルールです: - `userName` または `shoppingCart` のように人間が読みやすい名前を使ってください。 -- あなたが本当に何をしているか知っていない限り、 `a`, `b`, `c` のような略語や短い名前は避けてください。 -- 最大限説明的、かつ簡潔な名前を作ってください。悪い名前の例としては `data` や `value` です。このような名前は何も言いません。コンテキストからデータや値が意味することが例外的に明白な場合のみ使ってもOKです。 -- あなたのチームやあなた自身の心と、表現を合意してください。もしあるサイト訪問者が "ユーザ" と呼ばれる場合、関連する変数として `currentVisitor` や `newManInTown` ではなく `currentUser` や `newUser` のように命名するべきです。 +- 本当に何をしているか分かっている場合を除き、 `a`, `b`, `c` のような略語や短い名前は避けてください。 +- 最大限説明的、かつ簡潔な名前を作ってください。悪い名前の例としては `data` や `value` です。このような名前からは何も分かりません。コンテキストからデータや値が意味することが例外的に明白な場合のみ使ってもOKです。 +- あなたのチームの中で、またあなた自身の心の中で用語を統一してください。もしあるサイト訪問者が "ユーザ" と呼ばれる場合、関連する変数として `currentVisitor` や `newManInTown` ではなく `currentUser` や `newUser` のように命名するべきです。 -単純に聞こえますか? 確かにそうですが、実際には良い説明的で簡潔な名前を作ることは簡単ではありません。頑張りましょう。 +簡単に聞こえますか? 確かにそうですが、実際には良い説明的で簡潔な名前を作ることは簡単ではありません。頑張りましょう。 ```smart header="再利用 or 作成?" -そして最後のメモです。新しい変数宣言する代わりに、既存の変数を再利用する傾向のある怠けたプログラマが中にはいます。 +そして最後のメモです。新しい変数を宣言する代わりに、既存の変数を再利用する傾向のある怠けたプログラマが中にはいます。 -その結果、変数は、人々がステッカーの変更なしに異なったものを投げ入れるボックスのようになります。今その中は何でしょうか?誰が知っている...? 私たちはより細かくチエックする必要があります。 +その結果、変数はステッカーの変更なしに異なったものを投げ入れる箱になります。今何が入っているでしょうか?誰が知っているでしょうか...? より細かくチェックが必要になります。 このようなプログラマは変数定義では多少節約しますが、デバッグで10倍以上の時間を失います。 余分な変数は良く、悪ではないです。 -モダンなJavaScriptは minify したりブラウザは十分にコードを最適化します。なので、パフォーマンスの問題を作ることはありません。異なった値のために異なる変数を使うことは、エンジンの最適化を助けることがあります。 +モダンなJavaScriptは minify したり、ブラウザは十分にコードを最適化します。なので、パフォーマンスの問題になることはありません。異なる値に対して異なる変数を使うことは、エンジンの最適化を助けることもあります。 ``` ## サマリ -私たちはデータを格納するために変数を宣言することができます。それは `var`, `let`, または `const` を使うことでできます。 +データを格納するために変数を宣言することができます。それは `var`, `let`, または `const` を使うことでできます。 -- `let` -- は現代の変数宣言です。Chrome(V8)では、 `let` を使うには、そのコードは strict モードである必要があります。 -- `var` -- は伝統的なスタイルの変数宣言です。一般的にそれを使いませんが、必要なときのために、チャプター で `let` との微妙な違いを説明します。 +- `let` -- は現代の変数宣言です。Chrome(V8)では、`let` を使うには、そのコードは strict モードである必要があります。 +- `var` -- は伝統的なスタイルの変数宣言です。一般的には使いませんが、必要なときのために、チャプター で `let` との微妙な違いを説明します。 - `const` -- は `let` のようですが、変数の値は変更することができません。 変数は、内部のことを簡単に理解できるように命名するべきです。 diff --git a/1-js/02-first-steps/04-variables/variable-change.png b/1-js/02-first-steps/04-variables/variable-change.png deleted file mode 100644 index 6dd3803d97..0000000000 Binary files a/1-js/02-first-steps/04-variables/variable-change.png and /dev/null differ diff --git a/1-js/02-first-steps/04-variables/variable-change.svg b/1-js/02-first-steps/04-variables/variable-change.svg new file mode 100644 index 0000000000..1b26792380 --- /dev/null +++ b/1-js/02-first-steps/04-variables/variable-change.svg @@ -0,0 +1 @@ +"World!""Hello!"message \ No newline at end of file diff --git a/1-js/02-first-steps/04-variables/variable-change@2x.png b/1-js/02-first-steps/04-variables/variable-change@2x.png deleted file mode 100644 index f57b04ab16..0000000000 Binary files a/1-js/02-first-steps/04-variables/variable-change@2x.png and /dev/null differ diff --git a/1-js/02-first-steps/04-variables/variable.png b/1-js/02-first-steps/04-variables/variable.png deleted file mode 100644 index ab532d91d3..0000000000 Binary files a/1-js/02-first-steps/04-variables/variable.png and /dev/null differ diff --git a/1-js/02-first-steps/04-variables/variable.svg b/1-js/02-first-steps/04-variables/variable.svg new file mode 100644 index 0000000000..1c3d8b0cbe --- /dev/null +++ b/1-js/02-first-steps/04-variables/variable.svg @@ -0,0 +1 @@ +"Hello!"message \ No newline at end of file diff --git a/1-js/02-first-steps/04-variables/variable@2x.png b/1-js/02-first-steps/04-variables/variable@2x.png deleted file mode 100644 index c9c37f034e..0000000000 Binary files a/1-js/02-first-steps/04-variables/variable@2x.png and /dev/null differ diff --git a/1-js/02-first-steps/05-types/article.md b/1-js/02-first-steps/05-types/article.md index c8dab62b8f..d8c12edb52 100644 --- a/1-js/02-first-steps/05-types/article.md +++ b/1-js/02-first-steps/05-types/article.md @@ -1,6 +1,10 @@ # データ型 -JavaScriptの変数はどんなデータでも入れることができます。変数はある時点では文字列で、その後数値を受け取ることができます。: +JavaScript の値は常に特定の型です。例えば、文字列や数値です。 + +JavaScript には8つの基本的なデータ型があります。ここでは基本を学び、次のチャプターでそれらの詳細について話しましょう。 + +なお、変数はどんなデータでも入れることができます。変数はある時点では文字列で、その後数値を設定することができます: ```js // エラーなし @@ -10,10 +14,6 @@ message = 123456; このようなことができるプログラミング言語は "動的型付け" と呼ばれ、データ型はありますが、変数はそのどれにもバインドされないことを意味します。 -JavaScriptでは7つの基本的なデータ型があります。ここでは基本を学び、次のチャプターでそれらの詳細について話しましょう。 - -[cut] - ## 数値 ```js @@ -29,13 +29,13 @@ n = 12.345; - `Infinity` は数学的な[無限大](https://en.wikipedia.org/wiki/Infinity) ∞ を表します。どの値よりも大きい特別な値です。 - ゼロによる除算の結果でそれを得ることができます。: + ゼロによる除算でそれを得ることができます。: ```js run alert( 1 / 0 ); // 無限大 ``` - もしくは、単にコードで直接それを書くこともできます: + もしくは、単にコードに直接書くこともできます: ```js run alert( Infinity ); // 無限大 @@ -55,14 +55,38 @@ n = 12.345; そのため、数学的な表現の中のどこかに `NaN` がある場合、結果全体に伝搬します。 ```smart header="算術演算子は安全です" -JavaScriptでは数学をするのは安全です。ゼロによる除算、数値として数値ではない文字列を扱うなど何でもできます。 +JavaScriptでは数学をするのは安全です。ゼロによる除算、数値ではない文字列を数値として扱う、など何でもできます。 スクリプトは致命的なエラー("死")で止まることはありません。 最悪の場合でも NaN という結果になります。 ``` -特別な数値は正式には "数値" 型に所属します。もちろん、それらはこの言葉の常識では数値ではありません。 +特別な数値は正式には "数値" 型に所属します。もちろん、常識では数値ではありませんが。 + +チャプター でより数値の動作について見ていきます。 + +## BigInt -チャプター でより数値の動作について見るでしょう。 +JavaScript では、"数値" 型は (253-1) (つまり `9007199254740991`) より大きい、あるいは負値であれば -(253-1) より小さい整数を表現することができません。これらは内部表現に起因する技術的な制限です。 + +ほとんどの目的ではこれで十分ですが、場合によっては非常に大きな数が必要になります。例えば暗号化やマイクロ秒の精度のタイムスタンプです。 + +`BigInt` 型は任意の長さの整数を表現するために、最近言語に追加された型です。 + +`BigInt` の値は整数値の末尾に `n` を追加することで作成されます: + +```js +// 末尾の "n" は BigInt を意味します +const bigInt = 1234567890123456789012345678901234567890n; +``` + +`BigInt` の数値はめったに必要とされないのでここでは説明しませんが、別のチャプター で記載しています。このような大きな数値が必要なときは参照してください。 + + +```smart header="互換性の問題" +現時点では、`BigInt` は Firefox/Chrome/Edge/Safari でサポートされていますが、IE ではサポートされていません。 +``` + +[*MDN* BigInt compatibility table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) で、どのバージョンのブラウザがサポートしているか確認できます。 ## 文字列 @@ -74,7 +98,7 @@ let str2 = 'Single quotes are ok too'; let phrase = `can embed ${str}`; ``` -JavScriptでは3種類の引用符があります。 +JavaScriptでは3種類の引用符があります。 1. ダブルクォート: `"Hello"`. 2. シングルクォート: `'Hello'`. @@ -82,7 +106,7 @@ JavScriptでは3種類の引用符があります。 ダブル/シングルクォートは "シンプルな" 引用符です。JavaScriptの中ではそれらに違いはありません。 -バッククォートは "拡張機能" の引用符です。変数を `${…}` の中にラップすることで、変数を埋め込み、文字列の中で表現することができます。たとえば: +バッククォートは "拡張機能" の引用符です。変数を `${…}` の中にラップすることで、変数を埋め込み文字列の中で表現することができます。たとえば: ```js run let name = "John"; @@ -94,20 +118,19 @@ alert( `Hello, *!*${name}*/!*!` ); // Hello, John! alert( `the result is *!*${1 + 2}*/!*` ); // 結果は 3 ``` -`${…}` の中の表現は評価され、結果は文字列の一部になります。そこにはなんでも置くことができます: `name` のような変数、`1 + 2` のような算術表現、またはより複雑なものを書くことができます。 +`${…}` の中の表現は評価され、結果は文字列の一部になります。そこには何でも置くことができます: `name` のような変数、`1 + 2` のような算術表現、またはより複雑なものを書くこともできます。 これはバッククォートでのみ可能なことに留意してください。他のクォートはこのような埋め込みは許容しません! - ```js run alert( "the result is ${1 + 2}" ); // 結果は ${1 + 2} です(ダブルクォートは何もしません) ``` -チャプター で、より徹底的に文字列の説明します。 +チャプター で、より深く文字列の説明をします。 ```smart header="*character* 型はありません" -いくつかの言語では、1文字のための特別な "文字" 型があります。たとえば、C言語やJavaでは、それは `char` です。 +言語によっては、1文字のための特別な "文字" 型があります。たとえば、C言語やJavaでは、それは `char` です。 -JavaScriptでは、このような型はありません。 `string` 型の1つだけです。文字列は1つの文字または複数のそれらから構成されます。 +JavaScriptではこのような型はありません。`string` 型の1つなだけです。文字列は単一の文字または複数の文字から構成されます。 ``` ## boolean (論理型) @@ -123,7 +146,7 @@ let nameFieldChecked = true; // yes, 名前フィールドはチェックされ let ageFieldChecked = false; // no, 年齢フィールドは未チェック ``` -Boolean 値は比較の結果としても現れます: +Boolean 値は比較の結果としても使われます: ```js run let isGreater = 4 > 1; @@ -151,7 +174,7 @@ JavaScriptでは、 `null` は他の言語のような "存在しないオブジ ## "undefined" 値 -特殊な値 `undefined` も別に扱われます。`null` のように、それ自身の形を作ります。 +特殊な値 `undefined` も別に扱われます。`null` のように、それ自身の型を持ちます。 `undefined` の意味は "値は代入されていません" です。 @@ -166,24 +189,27 @@ alert(x); // "undefined" を表示 技術的にはどの変数にも `undefined` を代入することができます。 ```js run -let x = 123; +let age = 100; -x = undefined; +// 値を undefined に変更 +age = undefined; -alert(x); // "undefined" +alert(age); // "undefined" ``` -...しかし、そのようにするのは推奨されません。一般的には、 "空" や "不明な値" を書き込むために `null` を使い、`undefined` は変数が割り当てられているか、もしくは似たような確認のために使います。 +...しかし、そのようにするのは推奨されません。一般的には、 "空" や "不明な値" と言った用途では `null` を使い、`undefined` は変数が割り当てられているか、もしくは似たような確認のために使います。 ## オブジェクトとシンボル `object` 型は特殊です。 -他のすべての型は、値は1つのもの(文字列, 数値, または何でも)だけを含むことができるので、"プリミティブ" と呼ばれます。対象的に、オブジェクトはデータのコレクションやより複雑なエンティティを格納するために使われます。ここでは、プリミティブについて十分に知った後、それらを後ほどチャプターで扱います。 +他のすべての型は、値は1つのもの(文字列, 数値, または何でも)だけを含むので、"プリミティブ" と呼ばれます。対照的に、オブジェクトはデータのコレクションやより複雑なエンティティを格納するために使われます。 + +その重要性から、オブジェクトに関してはプリミティブについて詳しく学んだ後に、チャプターで扱います。 -`symbol` 型はオブジェクトの一意な識別子を作るのに使われます。完全性のために、私たちはここで言及する必要がありますが、オブジェクトの後で勉強するのがよいでしょう。 +`symbol` 型はオブジェクトの一意な識別子を作るのに使われます。完全性のためにここで言及していますが、オブジェクトの後で勉強するのがよいでしょう。 -## typeof 操作 +## typeof 演算子 `typeof` 操作は、引数の型を返します。異なる型の値を別々に処理したい、または素早くチェックしたいときに役立ちます。 @@ -201,6 +227,8 @@ typeof undefined // "undefined" typeof 0 // "number" +typeof 10n // "bigint" + typeof true // "boolean" typeof "foo" // "string" @@ -223,15 +251,15 @@ typeof alert // "function" (3) 最後の3行については追加の説明が必要かもしれません: 1. `Math` は数学的な操作を提供する組み込みオブジェクトです。チャプターで学ぶでしょう。ここでは、単にオブジェクトとしての例です。 -2. `typeof null` の結果は `"object"` です。これは間違っています。これは `typeof` において、公式に認められているエラーで、互換性のために維持されています。もちろん、`null` はオブジェクトではありまえん。それは自身の別の型をもつ特殊な値です。なので、繰り返しますがそれは言語のエラーです。 -3. `alert` は言語の機能なので、`typeof alert` の結果は `"function"` です。我々は次のチャプターで function を勉強します。そして、言語の中で特殊な "function" 型がないことがわかるでしょう。function はオブジェクト型に属します。しかし `typeof` はそれらを別々に扱います。正式にはそれは正しくありませんが、実際にはとても便利です。 - +2. `typeof null` の結果は `"object"` です。これは間違っています。これは `typeof` において、公式に認められているエラーで、互換性のために維持されています。もちろん、`null` はオブジェクトではありません。それは自身の別の型をもつ特殊な値です。なので、繰り返しますがそれは言語のエラーです。 +3. `alert` は言語の機能なので、`typeof alert` の結果は `"function"` です。我々は次のチャプターで function を勉強します。そして、言語の中には特別な "function" 型がないことがわかるでしょう。function はオブジェクト型に属します。しかし `typeof` はそれらを別々に扱います。正式にはそれは正しくありませんが、実際にはとても便利です。 ## サマリ JavaScriptには7つの基本型があります。 - `number` あらゆる種類の数値: 整数または浮動小数点 +- `bigint` は任意の長さの整数値のための型 - `string` 文字列。文字列は1つかより多くの文字を持ち、別の1文字型はありません。 - `boolean` `true`/`false` - `null` 不明な値 -- 単一の値 `null` を持つスタンドアロン型 diff --git a/1-js/02-first-steps/09-alert-prompt-confirm/1-simple-page/solution.md b/1-js/02-first-steps/06-alert-prompt-confirm/1-simple-page/solution.md similarity index 100% rename from 1-js/02-first-steps/09-alert-prompt-confirm/1-simple-page/solution.md rename to 1-js/02-first-steps/06-alert-prompt-confirm/1-simple-page/solution.md diff --git a/1-js/02-first-steps/09-alert-prompt-confirm/1-simple-page/task.md b/1-js/02-first-steps/06-alert-prompt-confirm/1-simple-page/task.md similarity index 100% rename from 1-js/02-first-steps/09-alert-prompt-confirm/1-simple-page/task.md rename to 1-js/02-first-steps/06-alert-prompt-confirm/1-simple-page/task.md diff --git a/1-js/02-first-steps/06-alert-prompt-confirm/article.md b/1-js/02-first-steps/06-alert-prompt-confirm/article.md new file mode 100644 index 0000000000..b923845e65 --- /dev/null +++ b/1-js/02-first-steps/06-alert-prompt-confirm/article.md @@ -0,0 +1,105 @@ +# インタラクション: alert, prompt, confirm + +デモ環境としてブラウザを使っているので、ユーザと対話するためのいくつかの関数を見ておきましょう: `alert`, `prompt` そして `confirm` です + +## alert + +既にご覧になったと思いますが、メッセージを表示し、ユーザが "OK" をクリックするのを待ちます。 + +例: + +```js run +alert("Hello"); +``` + +メッセージのある小さいウィンドウは *モーダルウィンドウ* と呼ばれます。"モーダル" という言葉は、そのウィンドウを処理するまで(今の場合であれば、OKボタンを押すまで)、訪問者はページの他の部分と対話したり、他のボタンを押すことができないことを意味します。 + +## prompt + +`prompt` 機能は2つの引数を受け入れます: + +```js no-beautify +result = prompt(title[, default]); +``` + +テキストメッセージ、訪問者のための入力フィールド、OK/CANCEL ボタンをもつ小窓を表示します。 + +`title` +: 訪問者へ表示するテキストです。 + +`default` +: 任意の2つ目のパラメータで、入力フィールドの初期値です。 + +```smart header="構文 `[...]` の角括弧" +構文中にある `default` を囲む角括弧は、パラメータがオプションであり必須ではないことを指します。 +``` + +訪問者はプロンプトの入力フィールドに何か入力し、OKを押すかもしれません。または CANCEL ボタンの押下、もしくは `key:Esc` キーにより入力をキャンセルすることができます。 + +`prompt` の呼び出しはフィールドのテキスト、もしくは入力がキャンセルされた場合には `null` が返却されます。 + +例: + +```js run +let age = prompt('How old are you?', 100); + +alert(`You are ${age} years old!`); // You are 100 years old! +``` + +````warn header="IE: 常に `デフォルト` を設定してください" +2つ目のパラメータは任意です。しかし、それを指定しない場合、Internet Explorer はプロンプトにテキスト `"undefined"` を挿入します。 + +確認する場合、Internet Explorer でこのコードを実行しましょう: + +```js run +let test = prompt("Test"); +``` + +なので IEで良く見えるようにするには、常に2つ目の引数を指定することが推奨されます。: + +```js run +let test = prompt("Test", ''); // <-- for IE +``` +```` + +## confirm + +構文: + +```js +result = confirm(question); +``` + +`confirm` 関数は `question` と 2つのボタンをもつモーダルウィンドウを表示します。: OK と キャンセル + +OK が押された場合の結果は `true` で、それ以外は `false` です。 + +例: + +```js run +let isBoss = confirm("Are you the boss?"); + +alert( isBoss ); // true OKが押された場合 +``` + +## サマリ + +訪問者とやり取りをするための3つのブラウザ固有の関数を説明しました。 + +`alert` +: メッセージを表示します + +`prompt` +: ユーザにテキスト入力を求めるメッセージを表示します。テキスト、もしくはCANCEL/ `key:Esc` がクリックされた場合はすべてのブラウザは `null` を返します。 + +`confirm` +: メッセージを表示し、ユーザが "OK" または "CANCEL" を押すのを待ちます。OKの場合は `true` を、CANCEL/`key:Esc` の場合は `false` を返します。 + +これらすべての関数はモーダルです: スクリプトの実行を中断し、メッセージが消えるまで訪問者がページの他の部分とやり取りするのを禁止します。 + +上のすべての関数で共有される2つの制限があります。: + +1. モーダルウィンドウの正確な位置はブラウザによって決定されます。通常それは中央です。 +2. ウィンドウの正確な見た目もまたブラウザに依存し、それを修正することはできません。 + +それは単純化に対する代償です。より良いウィンドウを表示し、訪問者とのよりリッチなインタラクションを実現する方法もありますが、"必要以上の装飾" が重要でない場合、これらの方法が使えます。 diff --git a/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/solution.md b/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/solution.md deleted file mode 100644 index d13c7e7987..0000000000 --- a/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/solution.md +++ /dev/null @@ -1,22 +0,0 @@ - -```js no-beautify -"" + 1 + 0 = "10" // (1) -"" - 1 + 0 = -1 // (2) -true + false = 1 -6 / "3" = 2 -"2" * "3" = 6 -4 + 5 + "px" = "9px" -"$" + 4 + 5 = "$45" -"4" - 2 = 2 -"4px" - 2 = NaN -7 / 0 = Infinity -" -9\n" + 5 = " -9\n5" -" -9\n" - 5 = -14 -null + 1 = 1 // (3) -undefined + 1 = NaN // (4) -``` - -1. 文字列 `"" + 1` の加算は `1` を文字列に変換します: `"" + 1 = "1"`, そして `"1" + 0` でも同じルールが適用されます。 -2. 減算 `"-"` は(ほとんどの算術演算子のように)数値のみで動作し、空文字列 `""` を `0` に変換します。 -3. `null` は数値変換後は `0` になります。 -4. `undefined` は数値変換後は `NaN` になります。 diff --git a/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/task.md b/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/task.md deleted file mode 100644 index 7a8e77f665..0000000000 --- a/1-js/02-first-steps/06-type-conversions/1-primitive-conversions-questions/task.md +++ /dev/null @@ -1,26 +0,0 @@ -importance: 5 - ---- - -# 型変換 - -これらの式の結果はどうなるでしょうか? - -```js no-beautify -"" + 1 + 0 -"" - 1 + 0 -true + false -6 / "3" -"2" * "3" -4 + 5 + "px" -"$" + 4 + 5 -"4" - 2 -"4px" - 2 -7 / 0 -" -9\n" + 5 -" -9\n" - 5 -null + 1 -undefined + 1 -``` - -よく考え、書き溜めてから答えと比較してみてください。 diff --git a/1-js/02-first-steps/06-type-conversions/article.md b/1-js/02-first-steps/06-type-conversions/article.md deleted file mode 100644 index d9df90a7b5..0000000000 --- a/1-js/02-first-steps/06-type-conversions/article.md +++ /dev/null @@ -1,163 +0,0 @@ -# 型変換 - -多くの場合、演算子と関数は自動的に値を正しい型に変換します。それを "型変換" と呼びます。 - -たとえば、 `alert` は表示するために、自動的にある値を文字列へ変換します。数学的な処理は値を数値に変換します。 - -また、物事を正しくするために、ある値を明示的に変換する必要があるケースもあります。 - -[cut] - -```smart header="まだオブジェクトについては話していません" -このチャプターでは、まだ オブジェクト は説明しません。ここでは最初にプリミティブを学びます。その後、オブジェクトについて学んだ後、チャプター で、どのようにオブジェクト変換が動作するのかを見ていきます。 -``` - -## 文字列変換 - -文字列変換は、文字列形式の値が必要なときに発生します。 - -たとえば、`alert(value)` は値を表示するためにそれを行います。 - -また、そのために、`String(value)` 関数を使うこともできます: - -```js run -let value = true; -alert(typeof value); // boolean - -*!* -value = String(value); // 今、値は文字列の "true" -alert(typeof value); // string -*/!* -``` - -文字列変換はほとんどが明らかです。`false` は `"false"` に、 `null` は `"null"` になります。 - -## 数値変換 - -数値変換は数学的関数や表現の中で自動的に起こります。 - -たとえば、非数値に除算 `/` が適用された場合: - -```js run -alert( "6" / "2" ); // 3, 文字列は数値に変換されます -``` - -また、明示的に `value` を変換するために `Number(value)` を使うことができます。 - -```js run -let str = "123"; -alert(typeof str); // string - -let num = Number(str); // 数値の 123 になります - -alert(typeof num); // number -``` - -通常、私たちがテキストフォームのような文字列ベースのソースから値を読むときに明示的な変換が必要になりますが、私たちは数値が入力されることを期待します。 - -もしも文字列が有効な数値出ない場合、このような変換の結果は `NaN` です。たとえば: - -```js run -let age = Number("an arbitrary string instead of a number"); - -alert(age); // NaN, 変換失敗 -``` - -数値変換ルール: - -| 値 | 変換後... | -|-------|-------------| -|`undefined`|`NaN`| -|`null`|`0`| -|true と false | `1` and `0` | -| `string` | 最初と最後のスペースは取り除かれます。そして、残った文字列が空の場合は結果は 0 になります。そうでなければ、文字列から "読んだ" 数値です。 エラーでは `NaN` が与えられます。| - -例: - -```js run -alert( Number(" 123 ") ); // 123 -alert( Number("123z") ); // NaN ("z" の読み込みでエラー) -alert( Number(true) ); // 1 -alert( Number(false) ); // 0 -``` - -`null` と `undefined` はここでは異なる振る舞いをすることに留意してください。: `undefined` が `NaN` になる一方、`null` は 0 になります。 - -````smart header="'+'は文字列を連結します" -ほぼすべての算術演算は値を数値に変換します。 注目すべき例外は加算 `+` です。もしも加算された値の1つが文字列である場合、他の値は文字列に変換されます。 - -そして、それらを連結(結合)します。: - -```js run -alert( 1 + '2' ); // '12' (右側が文字列) -alert( '1' + 2 ); // '12' (左側が文字列) -``` - -引数の1つが文字列の場合にのみそれは起こります。それ以外では値は数値に変換されます。 -```` - -## Boolean変換 - -真偽値(Boolean)変換はシンプルです。 - -論理演算(後ほど条件テストやそれらの他の種類を見ます)で起こりますが、`Boolean(value)` を呼ぶことで手動で実行させる事もできます。 - -変換ルール: - -- `0`, 空文字, `null`, `undefined` や `NaN` のように直感的に "空" の値は `false` になります。 -- 他の値は `true` になります。 - -例: - -```js run -alert( Boolean(1) ); // true -alert( Boolean(0) ); // false - -alert( Boolean("hello") ); // true -alert( Boolean("") ); // false -``` - -````warn header="注意してください: ゼロの文字列 `\"0\"` は `true` です" -幾つかの言語(すなわち PHP)は `”0”` を `false` として扱います。しかし、JavaScriptでは、非空文字は常に `true` です。 - -```js run -alert( Boolean("0") ); // true -alert( Boolean(" ") ); // スペースもまた true です (任意の非空文字は true) -``` -```` - - -## サマリ - -3つの最も広く使われている型変換があります: 文字列変換, 数値変換, -真偽値変換です。 - -**`文字列変換`** -- 何かを出力するときに発生し、`String(value)` で実行することができます。文字列への変換は、通常はプリミティブな値にとって明白です。 - -**`数値変換`** -- 算術演算で発生し、`Number(value)` で実行できます。 - -変換は次のルールに従います: - -| 値 | 変換後... | -|-------|-------------| -|`undefined`|`NaN`| -|`null`|`0`| -|true と false | `1` and `0` | -| `string` | 最初と最後のスペースは取り除かれます。そして、残った文字列が空の場合は結果は 0 になります。そうでなければ、文字列から "読んだ" 数値です。 エラーでは `NaN` が与えられます。| - -**`真偽値変換`** -- 論理演算で発生するか、`Boolean(value)` で実行できます。 - -次のルールに従います: - -| 値 | 変換後... | -|-------|-------------| -|`0`, `null`, `undefined`, `NaN`, `""` |`false`| -|それ以外の値| `true` | - - -それらのルールのほとんどは、理解し覚えるのが簡単です。人々が通常間違える注目すべき例外は: - -- `undefined` は文字列としては `NaN` です, `0` ではりません。 -- `"0"` と `" "` のようなスペースだけの文字列は真偽値としては true です。 - -オブジェクトについてはここでは説明しませんが、JavaScriptについての基本的なことを学んだら、オブジェクトに専念する の章の後半に戻ります。 diff --git a/1-js/02-first-steps/07-operators/1-increment-order/solution.md b/1-js/02-first-steps/07-operators/1-increment-order/solution.md deleted file mode 100644 index 856af12d38..0000000000 --- a/1-js/02-first-steps/07-operators/1-increment-order/solution.md +++ /dev/null @@ -1,17 +0,0 @@ - -答えは次の通りです: - -- `a = 2` -- `b = 2` -- `c = 2` -- `d = 1` - -```js run no-beautify -let a = 1, b = 1; - -alert( ++a ); // 2, プレフィックス形式は新しい値を返します -alert( b++ ); // 1, サフィックス形式は古い値を返します - -alert( a ); // 2, 1回インクリメントされています -alert( b ); // 2, 1回インクリメントされています -``` diff --git a/1-js/02-first-steps/07-operators/1-increment-order/task.md b/1-js/02-first-steps/07-operators/1-increment-order/task.md deleted file mode 100644 index f70e87e16d..0000000000 --- a/1-js/02-first-steps/07-operators/1-increment-order/task.md +++ /dev/null @@ -1,14 +0,0 @@ -importance: 5 - ---- - -# プレフィックス(接頭辞)とサフィックス(接尾辞)の形式 - -下のコード後、すべての変数 `a`, `b`, `c` と `d` の最終値はなんでしょう? - -```js -let a = 1, b = 1; - -let c = ++a; // ? -let d = b++; // ? -``` diff --git a/1-js/02-first-steps/07-operators/2-assignment-result/task.md b/1-js/02-first-steps/07-operators/2-assignment-result/task.md deleted file mode 100644 index 6dc2e9192b..0000000000 --- a/1-js/02-first-steps/07-operators/2-assignment-result/task.md +++ /dev/null @@ -1,13 +0,0 @@ -importance: 3 - ---- - -# 代入の結果 - -下のコード後、`a` と `x` の値はなんでしょう? - -```js -let a = 2; - -let x = 1 + (a *= 2); -``` diff --git a/1-js/02-first-steps/07-operators/article.md b/1-js/02-first-steps/07-operators/article.md deleted file mode 100644 index 72581db844..0000000000 --- a/1-js/02-first-steps/07-operators/article.md +++ /dev/null @@ -1,443 +0,0 @@ -# 演算子 - -多くの演算子は学校で学んできたので知られています。加算 `+`, 乗算 `*`, 減算 `-` などです。 - -このチャプターでは、学校の算数でカバーされない側面に集中します。 - -[cut] - -## 用語: "単項演算子"、 "バイナリ"、 "オペランド" - -次に進む前に、一般的な用語を理解しましょう。 - -- *オペランド* -- は演算子が適用されるものです。たとえば、 乗算 `5 * 2` では、2つのオペランドがあります: 左のオペランドは `5`, 右のオペランドは `2` です。時々、人々は "オペランド" のかわりに "引数" といいます。 -- 演算子が単一のオペランドをもつ場合は *単項演算子* です。たとえば、単行否定 `"-"` は数値の符号を反転します: - - ```js run - let x = 1; - - *!* - x = -x; - */!* - alert( x ); // -1, 単項否定が適用されました - ``` -- 演算子が2つのオペランドを持つ場合は *バイナリ* です。同じマイナスもバイナリ形式で同様に存在します: - - ```js run no-beautify - let x = 1, y = 3; - alert( y - x ); // 2, バイナリマイナスは値を減算します - ``` - - 正式には、ここでは2つの異なった演算子について話しています。: 単行否定(1つのオペランド, 符号の反転) とバイナリ減算(2つのオペランド、減算) - -## 文字列の連結、バイナリ + - -さて、学校の算術以外の JavaScript 演算子の特別な機能を見ていきましょう。 - -通常、プラス演算子 `+` は数値の合計です。 - -しかし、もしもバイナリ `+` が文字列に適用された場合、それはそれらのマージ(連結)です。: - -```js -let s = "my" + "string"; -alert(s); // mystring -``` - -もしもいずれかのオペランドが文字列の場合、他のオペランドも文字列に変換されることに注意してください。 - -例: - -```js run -alert( '1' + 2 ); // "12" -alert( 2 + '1' ); // "21" -``` - -見てください、最初のオペランドが文字列か、2つ目のものがそうかは関係ありません。このルールはシンプルです:もしもどちらかのオペランドが文字列の場合、同様に他の一方も文字列に変換します。 - -文字列連結や変換はバイナリプラス `"+"` の特別な機能です。他の算術演算子は数値でのみ動作します。それらはいつもオペランドを数値に変換します。 - -たとえば、減算と除算です: - -```js run -alert( 2 - '1' ); // 1 -alert( '6' / '2' ); // 3 -``` - -## 数値変換 単項 + - -プラス `+` は2つの形で存在します。上で使ったようなバイナリ形式と単項形式です。 - -単項プラス、もしくは言い換えると単一の値に適用されるプラス演算子 `+` は、数値に対しては何もしません。しかし、オペランドが数値でない場合は数値に変換します。 - -例: - -```js run -// 数値には何の影響もありません -let x = 1; -alert( +x ); // 1 - -let y = -2; -alert( +y ); // -2 - -*!* -// 非数値を変換 -alert( +true ); // 1 -alert( +"" ); // 0 -*/!* -``` - -それは実際には、 `Number(...)` と同じですが、より短い表現です。 - -文字列から数値に変換する必要なケースは多いです。HTMLのフォームフィールドから値を取得する場合、それらは通常文字列です。 - -今、それらの合計が欲しい場合はどうなるでしょう? - -バリナリプラスはそれらを文字列として追加します。: - -```js run -let apples = "2"; -let oranges = "3"; - -alert( apples + oranges ); // "23", バイナリプラスは文字列を連結します -``` - -それらを文字列として扱いたい場合、変換してそれらを合計することが出来ます: - -```js run -let apples = "2"; -let oranges = "3"; - -*!* -// バイナリプラスの前に両方の値が数値に変換されます -alert( +apples + +oranges ); // 5 -*/!* - -// 長いバリアント -// alert( Number(apples) + Number(oranges) ); // 5 -``` - -数学者の立場からは、余計なプラスは奇妙に見えるかもしれません。しかし、プログラマの立場からは特殊なことではありません: 単項プラスが最初に適用され、文字列から数値に変換されます。次にバイナリプラスはそれらを合計します。 - -なぜバイナリプラスの前に単項プラスが値に適用されるのでしょうか?それは、単項プラスの *優先順位が高いため* です。 - -## 演算子の優先順位 - -式が1つ以上の演算子をもつ場合、実行順はそれらの *優先順位* により定義されています。言い換えると、演算子の間には暗黙の優先順があります。 - -学校から、私たちはみんな、式 `1 + 2 * 2` での乗算は加算の前に計算されるべきであることを知っています。それがまさに優先順位です。乗算は加算より *より高い優先順位* と言われています。 - -丸括弧はどの優先順位もオーバーライドするので、実行順序が不満な場合は丸括弧使うことができます、このように: `(1 + 2) * 2`。 - -JavaScriptでは多くの演算子があります。どの演算子も対応する優先順位を持っています。より大きな値をもつ演算子は最初に実行されます。同じ優先順の場合、実行順は左から右になります。 - -[優先順位テーブル](https://developer.mozilla.org/en/JavaScript/Reference/operators/operator_precedence)の抜粋(これを覚えておく必要はありませんが、単項演算子は対応するバイナリ演算子よりも優先順位が高いことに留意してください): - - -| 優先順位 | 名前 | 符号 | -|------------|------|------| -| ... | ... | ... | -| 16 | 単項プラス | `+` | -| 16 | 単項否定 | `-` | -| 14 | 乗算 | `*` | -| 14 | 除算 | `/` | -| 13 | 加算 | `+` | -| 13 | 減算 | `-` | -| ... | ... | ... | -| 3 | 代入 | `=` | -| ... | ... | ... | - -ご覧の通り、 "単項プラス" は `16` の優先順位を持っており、それは "加算" の `13` よりも大きいです(バイナリプラス)。なので、式 `"+apples + +oranges"` において、単項プラスは最初に動作し、次に加算になります。 - -## 代入 - -代入 `=` もまた演算子であることに注意しましょう。 `3`というとても低い値として優先順位テーブルにリストされています。 - -そういうわけで、`x = 2 * 2 + 1` のように変数を代入するとき、計算が最初に行われ、その後 `=` が評価され、 `x` に結果が格納されます。 - -```js -let x = 2 * 2 + 1; - -alert( x ); // 5 -``` - -代入をチェーンすることもできます: - -```js run -let a, b, c; - -*!* -a = b = c = 2 + 2; -*/!* - -alert( a ); // 4 -alert( b ); // 4 -alert( c ); // 4 -``` - -チェーンされた代入は右から左へ評価されます。最初に最も右の式 `2 + 2` が評価され、次に左の変数に代入されます。: `c`, `b` と `a`です。 -最後に、全ての変数は単一の値で共有されます。 - -````smart header="代入演算子 `\"=\"` は値を返します" -演算子は常に値を返します。それは加算 `+` または 乗算 `*` のようにほとんどの場合明らかです。しかし代入演算子もそのルールに従います。 - -`x = value` の呼び出しは `value` を `x` に書き込み、 *それを返却します* - -これは、より複雑な式の一部として代入を使ったデモです: - -```js run -let a = 1; -let b = 2; - -*!* -let c = 3 - (a = b + 1); -*/!* - -alert( a ); // 3 -alert( c ); // 0 -``` - -上の例は、`(a = b + 1)` の結果は `a` に代入された値です(それは `3` です)。次にそれは `3` から減算するために使われます。 - -面白いコードですよね?サードパーティーのライブラリで見かけることがあるので、それがどのように動作するかは理解しておくのがよいでしょう。 -しかし、このようなものは書くべきではありません。このようなトリックは明らかにコードの可読性を損ないます。 -```` - -## 剰余 % - -剰余演算子 `%` はその見た目にもかかわらず、パーセントと関係はありません。 - -`a % b` の結果は、`b` による `a` の割り数割り算の余りです。 - -例: - -```js run -alert( 5 % 2 ); // 1は5を2で割った余りです -alert( 8 % 3 ); // 2は8を3で割った余りです -alert( 6 % 3 ); // 0は6を3で割った余りです -``` - -## べき乗 ** - -べき乗演算子 `**` は最近言語に追加されています。 - -自然数 `b` において、`a ** b` の結果は、`b` の回数だけ自身により乗算された `a` です。 - -例: - -```js run -alert( 2 ** 2 ); // 4 (2 * 2) -alert( 2 ** 3 ); // 8 (2 * 2 * 2) -alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2) -``` - -演算子は非整数値の `a` や `b` でも同様に動作します。例えば: - -```js run -alert( 4 ** (1/2) ); // 2 (1/2のべき乗は平方根と同じです、それは数学です) -alert( 8 ** (1/3) ); // 2 (1/3のべき乗は、立方根と同じです) -``` - -## インクリメント/デクリメント - - - -数値を1ずつ増減することは、最も一般的な数値演算の1つです。 - -なので、そのための特別な演算子があります: - -- **インクリメント** `++` 変数を1増加させる: - - ```js run no-beautify - let counter = 2; - counter++; // counter = counter + 1 と同じですがより短いです - alert( counter ); // 3 - ``` -- **デクリメント** `--` 変数を1減少させる: - - ```js run no-beautify - let counter = 2; - counter--; // counter = counter - 1 と同じですがより短いです - alert( counter ); // 1 - ``` - -```warn -インクリメント/デクリメントは変数に対してのみ適用可能です。 それを `5++` のように値に対して使おうとすると、エラーになります。 -``` - -演算子 `++` と `--` は変数の前後両方に配置することができます。 - -- 演算子が変数の後にある場合、それは "後置式" と呼ばれます: `counter++`。 -- "前置式" は演算子が変数の前に来るときです: `++counter`。 - -結果はどちらも同じです: `counter` を `1` 増加します。 - -それらに違いはありますか?はい、しかし `++/--` の戻り値を使う場合、その値しかみることができません。 - -違いを明確にしましょう。ご存知の通り、全ての演算子は値を返します。ここではインクリメント/デクリメントも例外ではありません。前置式は新しい値を返す一方、後置式は古い値を返します(インクリメント/デクリメントの前) - -違いを見るために、ここに例があります: - -```js run -let counter = 1; -let a = ++counter; // (*) - -alert(a); // *!*2*/!* -``` - -ここで `(*)` の行で前置呼び出し `++counter` は `counter` を増加させ、`2` という新しい値を返します。そのため、 `alert` は `2` を表示します。 - -後置式を使いましょう: - -```js run -let counter = 1; -let a = counter++; // (*) ++counter を counter++ に変更 - -alert(a); // *!*1*/!* -``` - -`(*)` の行で、 *後置* 式 `counter++` は `counter` を増加しますが、 *古い* 値を返します(増加する前)。そのため、 `alert` は `1` を表示します。 - -要約する: - -- もしもインクリメント/デクリメントの結果を使わない場合、どちらの形式を使う場合でも違いはありません。: - - ```js run - let counter = 0; - counter++; - ++counter; - alert( counter ); // 2, 上の行は同じことをします - ``` -- もしも値を増加に *加えて*、すぐに演算子の結果を使いたい場合は、前置式が必要になります: - - ```js run - let counter = 0; - alert( ++counter ); // 1 - ``` -- もしも増加するが、以前の値を使いたい場合は後置式が必要です: - - ```js run - let counter = 0; - alert( counter++ ); // 0 - ``` - -````smart header="他の演算子の中でのインクリメント/デクリメント" -演算子 `++/--` は同様に式の中でも使うことができます。それらの優先順位は他の算術演算子よりも高いです。 - -例: - -```js run -let counter = 1; -alert( 2 * ++counter ); // 4 -``` - -比較: - -```js run -let counter = 1; -alert( 2 * counter++ ); // 2, counter++ は "古い" 値を返すからです -``` - -技術的には許容されますが、このような記法は一般的に、コードの可読性を下げます。1行で複数のことを行う -- よいことではありません。 - -コードを読んでいる間、速い "縦の" 目視はこのような `counter++` を見逃しやすく、また変数の増加が明白ではありません。 - -"1行は1アクション" のスタイルが推奨されます: - -```js run -let counter = 1; -alert( 2 * counter ); -counter++; -``` -```` - -## ビット演算子 - -ビット演算子は引数を 32ビットの整数値として扱い、それらのバイナリ表現のレベルで処理します。 - -それらの演算子はJavaScript固有のものではありません。ほとんどのプログラミング言語でサポートされています。 - -演算子のリスト: - -- AND ( `&` ) -- OR ( `|` ) -- XOR ( `^` ) -- NOT ( `~` ) -- LEFT SHIFT ( `<<` ) -- RIGHT SHIFT ( `>>` ) -- ZERO-FILL RIGHT SHIFT ( `>>>` ) - -これらの演算子はめったに使われません。それらを理解するためには、私たちは低レベルの数値表現について掘り下げるべきであり、それは現時点では最適ではないでしょう。特に、すぐには必要ないからです。もしもあなたが興味をもっている場合は、MDNの[ビット演算子 ](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators)の記事を読むことが出来ます。実際に必要になったときにそれをするのがより現実的でしょう。 - - -## その場で変更する - -私たちは、しばしば変数に演算子を適用し、その中に新しい値を格納する必要があります。 - -例: - -```js -let n = 2; -n = n + 5; -n = n * 2; -``` - -この表記法は演算子 `+=` と `*=` を使うことで短縮することができます: - -```js run -let n = 2; -n += 5; // 今 n = 7 (n = n + 5 と同じ) -n *= 2; // 今 n = 14 (n = n * 2 と同じ) - -alert( n ); // 14 -``` - -短い "変更と代入" 演算子はすべての算術演算子とビット演算子で存在します。: `/=`, `-=` など。 - -このような演算子は通常の代入として同じ優先順位を持っています。そのため、ほとんどの他の計算の後に実行します: - -```js run -let n = 2; - -n *= 3 + 5; - -alert( n ); // 16 (右のパートが最初に評価されるので、 n *= 8 と同じです) -``` - -## カンマ - -カンマ演算子 `','` は最もレアで普通ではない演算子の1つです。より短いコード書くために使われることがありますので、何が起こっているのか理解するために知っておく必要があります。 - -カンマ演算子を使うことで、いくつかの式カンマ `','` で分割して評価することを可能にします。それぞれが評価されますが、最後の1つの結果のみが返却されます。 - -例: - -```js run -*!* -let a = (1 + 2, 3 + 4); -*/!* - -alert( a ); // 7 (3 + 4 の結果) -``` - -ここで、最初の式 `1 + 2` は評価され、その結果はどこかへ捨てられます。次に `3 + 4` が評価され、結果として返却されます。 - -```smart header="カンマはとても優先順が低いです" -カンマ演算子はとても優先順位が低いことに注意してください。 `=` よりも低いため、上の例では丸括弧が重要です。 - -それらがない場合: `a = 1 + 2, 3 + 4` は `+` を最初に評価し、数値を `a = 3, 7` に加算します。次に代入演算子 `=` が `a = 3` を割り当てます。そして、カンマのあとの `7` は処理されず、無視されます。 -``` - -なぜこのような最後の部分を除いてすべてを捨てる演算子が必要なのでしょうか? - -より複雑な構造において、1行で複数のアクションを書くときに使用される場合があります。 - -例: - -```js -// 1行に3つの演算子 -for (*!*a = 1, b = 3, c = a * b*/!*; a < 10; a++) { - ... -} -``` - -このようなトリックは多くのJavaScriptフレームワークで利用されているため、ここで言及しています。しかし通常それらはコードの可読性を改善しません。なので、そのように書く前によく考えるべきです。 diff --git a/1-js/02-first-steps/07-type-conversions/article.md b/1-js/02-first-steps/07-type-conversions/article.md new file mode 100644 index 0000000000..fcfe4fd090 --- /dev/null +++ b/1-js/02-first-steps/07-type-conversions/article.md @@ -0,0 +1,150 @@ +# 型変換 + +多くの場合、演算子と関数は自動的に値を正しい型に変換します。それを "型変換" と呼びます。 + +たとえば `alert` は、表示のためにどのような値も文字列へと自動的に変換します。数学的な演算では、値は数値に変換されます。 + +また、物事を正しくするために、ある値を明示的に変換する必要がある場合もあります。 + +```smart header="まだオブジェクトについては話していません" +このチャプターでは、まだ オブジェクト は説明しません。ここでは最初にプリミティブを学びます。 + +その後、オブジェクトについて学んだ後、チャプター で、どのようにオブジェクト変換が動作するのかを見ていきます。 +``` + +## 文字列変換 + +文字列変換は、文字列形式の値が必要なときに発生します。 + +たとえば、`alert(value)` は値を表示するためにそれを行います。 + +また、そのために、`String(value)` 関数を使うこともできます: + +```js run +let value = true; +alert(typeof value); // boolean + +*!* +value = String(value); // 今、値は文字列の "true" +alert(typeof value); // string +*/!* +``` + +文字列変換はほとんどが明白です。`false` は `"false"` に、 `null` は `"null"` になります。 + +## 数値変換 + +数値変換は数学的関数や表現の中で自動的に起こります。 + +たとえば、非数値に除算 `/` が適用された場合: + +```js run +alert( "6" / "2" ); // 3, 文字列は数値に変換されます +``` + +また、明示的に `value` を変換するために `Number(value)` を使うことができます。 + +```js run +let str = "123"; +alert(typeof str); // string + +let num = Number(str); // 数値の 123 になります + +alert(typeof num); // number +``` + +テキストフォームのような、文字列ベースのソースから値を読むが、数値が入力されることを想定するときには通常明示的な変換が必要になります。 + +文字列が有効な数値でない場合、このような変換の結果は `NaN` です。たとえば: + +```js run +let age = Number("an arbitrary string instead of a number"); + +alert(age); // NaN, 変換失敗 +``` + +数値変換ルール: + +| 値 | 変換後... | +|-------|-------------| +|`undefined`|`NaN`| +|`null`|`0`| +|true と false | `1` and `0` | +| `string` | 最初と最後のスペースは取り除かれます。そして、残った文字列が空の場合は結果は 0 になります。そうでなければ、文字列から "読んだ" 数値です。 エラーでは `NaN` が与えられます。| + +例: + +```js run +alert( Number(" 123 ") ); // 123 +alert( Number("123z") ); // NaN ("z" の読み込みでエラー) +alert( Number(true) ); // 1 +alert( Number(false) ); // 0 +``` + +`null` と `undefined` はここでは異なる振る舞いをすることに留意してください。: `undefined` が `NaN` になる一方、`null` は 0 になります。 + +ほとんどの算術演算もこのような変換を行います。次のチャプターでそれらを詳しく見ていきます。 + +## Boolean変換 + +真偽値(Boolean)変換はシンプルです。 + +論理演算(後ほど条件テストや他の種類を見ます)で起こりますが、`Boolean(value)` を呼ぶことで手動で実行することもできます。 + +変換ルール: + +- `0`, 空文字, `null`, `undefined` や `NaN` のように直感的に "空" の値は `false` になります。 +- 他の値は `true` になります。 + +例: + +```js run +alert( Boolean(1) ); // true +alert( Boolean(0) ); // false + +alert( Boolean("hello") ); // true +alert( Boolean("") ); // false +``` + +````warn header="注意してください: ゼロの文字列 `\"0\"` は `true` です" +幾つかの言語(すなわち PHP)は `”0”` を `false` として扱います。しかし、JavaScriptでは、非空文字は常に `true` です。 + +```js run +alert( Boolean("0") ); // true +alert( Boolean(" ") ); // スペースもまた true です (任意の非空文字は true) +``` +```` + +## サマリ + +3つの最も広く使われている型変換があります: 文字列変換, 数値変換, 真偽値変換です。 + +**`文字列変換`** -- 何かを出力するときに起こり、`String(value)` で行うことができます。文字列への変換は、通常はプリミティブな値にとって明白です。 + +**`数値変換`** -- 算術演算で起こり、`Number(value)` で実行できます。 + +変換は次のルールに従います: + +| 値 | 変換後... | +|-------|-------------| +|`undefined`|`NaN`| +|`null`|`0`| +|true と false | `1` と `0` | +| `string` | 前後の連続した空白は取り除かれます。そして、残った文字列が空の場合は結果は 0 になります。そうでなければ、文字列から "読んだ" 数値です。 エラーでは `NaN` が与えられます。| + +**`真偽値変換`** -- 論理演算で発生するか、`Boolean(value)` で実行できます。 + +次のルールに従います: + +| 値 | 変換後... | +|-------|-------------| +|`0`, `null`, `undefined`, `NaN`, `""` |`false`| +|それ以外の値| `true` | + + +ルールのほとんどは理解し覚えるのが簡単です。通常間違える注目すべき例外は: + +- `undefined` は文字列としては `NaN` です, `0` ではりません。 +- `"0"` と `" "` のようなスペースだけの文字列は真偽値としては true です。 + +オブジェクトについてはここでは説明しませんが、JavaScriptについての基本的なことを学んだら、オブジェクトに専念する の章の後半に戻ります。 diff --git a/1-js/02-first-steps/08-comparison/article.md b/1-js/02-first-steps/08-comparison/article.md deleted file mode 100644 index 344b4ebd8b..0000000000 --- a/1-js/02-first-steps/08-comparison/article.md +++ /dev/null @@ -1,216 +0,0 @@ -# 比較 - -私たちが数学で知っている多くの比較演算子があります。: - -- より大きい/より小さい: a > b, a < b -- 大きい/小さいまたは等しい: a >= b, a <= b. -- 等位チェックは `a == b` として書かれます(2重の等式記号 `'='` に注意してください。1つのシンボル `a = b` は代入を意味します。) -- 等しくない。数学において、この記法は です。JavaScriptにおいては、感嘆符がその前についた代入として書かれます: a != b. - -[cut] - -## 結果はBoolean - -他のすべての演算子と同様、比較も値を返却します。値はBoolean型です。 - -- `true` -- "はい", "正しい" もしくは "真" を意味します. -- `false` -- "いいえ", "誤り" もしくは "偽" を意味します. - -例: - -```js run -alert( 2 > 1 ); // true (正しい) -alert( 2 == 1 ); // false (誤り) -alert( 2 != 1 ); // true (正しい) -``` - -任意の値のように、比較結果は変数に代入することができます: - -```js run -let result = 5 > 4; // 比較の結果を代入 -alert( result ); // true -``` - -## 文字列比較 - -どちらの文字列がより大きいかを見る場合、いわゆる "辞書" もしくは "数学における辞書式順序" の順序が使われます。 - -言い換えると、文字列は文字単位で比較されます。 - -例: - -```js run -alert( 'Z' > 'A' ); // true -alert( 'Glow' > 'Glee' ); // true -alert( 'Bee' > 'Be' ); // true -``` - -2つの文字列を比較するアルゴリズムはシンプルです: - -1. 両方の文字列の最初の文字を比較します。 -2. もしも1つ目のほうが大きい(もしくは小さい)場合、1つ目の文字列は2つ目の文字列よりも大きい(もしくは小さい)です。それで完了です。 -3. そうではなく、もしも最初の文字が等しい場合は、同じ方法で2つ目の文字を比較します。 -4. 文字列の最後までそれを繰り返します。 -5. もしも両方の文字列が同時に終わった場合、それらは等しいです。そうでなければ長い文字列がより大きいです。 - -上の例において、比較 `'Z' > 'A'` は最初のステップで結果を得ます。 - -文字列 `"Glow"` と `"Glee"` は文字単位の比較がされます。 - -1. `G` は `G` と同じ. -2. `l` は `l` と同じ. -3. `o` は `e` より大きい. ここでストップ. 1つ目の文字列のほうが大きいです. - -```smart header="実際の辞書ではなく、Unicode順です -上で与えられている比較アルゴリズムは、本の辞書や電話帳で使われているのとおおよそ同じです。しかし全く同じではありません。 - -例えば、大文字小文字の問題。大文字 `"A"` は小文字の `"a"` とは同じではありません。どちらがより大きいでしょう?実際は、小文字 `"a"` です。なぜでしょう?なぜなら、小文字は内部のエンコーディングテーブル(Unicode)でより大きな索引を持っているからです。チャプター で具体的な詳細と結果を取り上げます。 -``` - -## 異なる型の比較 - -異なった型に所属している値を比較するとき、それらは数値に変換されます。 - -例: - -```js run -alert( '2' > 1 ); // true, 文字列 '2' は数値 2 になります -alert( '01' == 1 ); // true, 文字列 '01' は数値 1 になります -``` - -真偽値の場合、`true` は `1` になり、 `false` は `0` になります。: - -```js run -alert( true == 1 ); // true -alert( false == 0 ); // true -``` - -````smart header="興味深い結果" -次の2つが同時に発生する場合があります: - -- 2つの値が等しい -- それらの一方は真偽値の `true` で、もう一方は真偽値の `false` - -例: - -```js run -let a = 0; -alert( Boolean(a) ); // false - -let b = "0"; -alert( Boolean(b) ); // true - -alert(a == b); // true! -``` - -JavaScriptの立場からすると、それは普通です。等価チェックは数値変換を使って変換をします(したがって、`"0"` は `0` になります)。 -一方、 `真偽値` 変換は別のルールセットを利用します。 -```` - -## 厳密な等価 - -通常の等価チェック `"=="` は問題を持っています。`0` と `false` を異なるものと判断させることはできません: - -```js run -alert( 0 == false ); // true -``` - -空文字列でも同じです: - -```js run -alert( '' == false ); // true -``` - -それは、異なる型のオペランドは等価演算子 `==` によって数値に変換されるためです。空文字は、ちょうど `false` のようにゼロになります。 - -もしも `0` と `false` を分けたい場合、どうすべきでしょうか? - -**厳密等価演算子 `===` は型変換なしで等価をチェックします。** - -言い換えると、もしも `a` と `b` が異なる型の場合、`a === b` はそれらの変換の試みをすることなく、すぐに `false` を返します。 - -試してみましょう: - -```js run -alert( 0 === false ); // false, 型が異なるためです -``` - -`!=` の類似として、"厳密な非等価" 演算子 `!==` もまた存在します。 - -厳密等価チェック演算子は書くのが少し長いですが、起こっていることを明らかにし、エラーの余地を少なくします。 - -## null と undefined の比較 - -より多くのエッジケースを見てみましょう。 - -`null` もしくは `undefined` が他の値と比較される場合、非直感的な振る舞いになります。 - -厳密な等価チェック `===` の場合 -: それぞれ、それ自身の別々の型に所属しているため、これらの値は異なります。 - -```js run -alert( null === undefined ); // false -``` - -非厳密なチェック `==` の場合 -: 特別なルールがあります。それら2つは "甘いカップル" です: それらはお互いに等しくなります(`==` の意味で)が、価値はありません。 - -```js run -alert( null == undefined ); // true -``` - -数学や他の比較 `< > <= >=` -: 値 `null/undefined` は数値に変換されます: `null` は `0` になり、`undefined` は `NaN` になります。 - -今、それらのルールを適用した時に起こる面白いことを見てみましょう。そして、より重要なことはこれらの機能でトラップに陥らない方法です。 - -### 奇妙な結果: null vs 0 - -`null` とゼロを比較してみましょう: - -```js run -alert( null > 0 ); // (1) false -alert( null == 0 ); // (2) false -alert( null >= 0 ); // (3) *!*true*/!* -``` - -はい、数学的にはそれらは奇妙です。最後の結果は "`null` はゼロより大きいまたは等しい" ことを述べています。そして上の比較の1つは正しいはずですが、それらは両方とも false です。 - -その理由は等価チェック `==` と比較 `> < >= <=` は異なった処理するためです。比較 `null` を数値に変換します、したがって `0` として扱います。そういう訳で (3) `null >= 0` は true で、 (1) は false になります。 - -一方、`undefined` と `null` の等価チェック `==` はいずれの変換もなしにルールによって処理します。それらはお互い等価で他のいずれとも等価ではありません。そういうわけで (2) `null == 0` は false です。 - -### 比べるもののない undefined - -値 `undefined` は比較において参加するべきではありません。: - -```js run -alert( undefined > 0 ); // false (1) -alert( undefined < 0 ); // false (2) -alert( undefined == 0 ); // false (3) -``` - -なぜそこまでゼロが嫌いなのでしょう?常に false です! - -このような結果になった理由は次の通りです: - -- 比較 `(1)` と `(2)` は、 `undefined` は `NaN` に変換されるため `false` を返します。また、`NaN` はすべての比較で `false` を返す特別な数値です。 -- `undefined` は `null` とのみ等価で、それ以外とは等価ではないため、等価チェック `(3)` は `false` を返します。 - -### 問題を回避する - -なぜそれらのサンプルを観察したのでしょう?我々は常にこれらの特殊性を覚えておく必要がありますか?まぁ、実際にはありません。 -実際には、それらのトリッキーなことは、時間とともに馴染みの深いものになっていくでしょう。しかし問題を回避するための確実な方法があります。 - -例外的な注意を払って、厳密な等価 `===` を除き `undefined/null` の比較を行うだけです。 - -あなたがしていることが本当に正しい場合でない限り、`null/undefined` かもしれない変数に対して比較 `>= > < <=` は使ってはいけません。 -もしも変数がこのような値を持つ可能性がある場合は、それらを別々にチェックしてください。 - -## サマリ - -- 比較演算子は論理値を返します。 -- 文字列は "辞書” 順で、1文字ずつ比較されます。 -- 異なった型の値が比較される場合、それらは数値に変換されます(厳密な等価チェックを除く) -- 値 `null` と `undefined` はそれぞれ等価 `==` であり、それ以外の値とは等価ではありません。 -- `>` または `<` のような比較を、時には `null/undefined` になるような変数に対して使う場合は注意してください。`null/undefined` を別々にチェックすることは良いアイデアです。 diff --git a/1-js/02-first-steps/08-operators/1-increment-order/solution.md b/1-js/02-first-steps/08-operators/1-increment-order/solution.md new file mode 100644 index 0000000000..cd510145ac --- /dev/null +++ b/1-js/02-first-steps/08-operators/1-increment-order/solution.md @@ -0,0 +1,17 @@ + +答えは次の通りです: + +- `a = 2` +- `b = 2` +- `c = 2` +- `d = 1` + +```js run no-beautify +let a = 1, b = 1; + +alert( ++a ); // 2, 前置式は新しい値を返します +alert( b++ ); // 1, 後置式は古い値を返します + +alert( a ); // 2, 1回インクリメントされています +alert( b ); // 2, 1回インクリメントされています +``` diff --git a/1-js/02-first-steps/08-operators/1-increment-order/task.md b/1-js/02-first-steps/08-operators/1-increment-order/task.md new file mode 100644 index 0000000000..9c07e5f7ef --- /dev/null +++ b/1-js/02-first-steps/08-operators/1-increment-order/task.md @@ -0,0 +1,14 @@ +importance: 5 + +--- + +# プレフィックス(接頭辞)とサフィックス(接尾辞)の形式 + +下のコードが実行されたあと、変数 `a`, `b`, `c`, `d` はいくつになるでしょう? + +```js +let a = 1, b = 1; + +let c = ++a; // ? +let d = b++; // ? +``` diff --git a/1-js/02-first-steps/07-operators/2-assignment-result/solution.md b/1-js/02-first-steps/08-operators/2-assignment-result/solution.md similarity index 100% rename from 1-js/02-first-steps/07-operators/2-assignment-result/solution.md rename to 1-js/02-first-steps/08-operators/2-assignment-result/solution.md diff --git a/1-js/02-first-steps/08-operators/2-assignment-result/task.md b/1-js/02-first-steps/08-operators/2-assignment-result/task.md new file mode 100644 index 0000000000..4b6db6a9e1 --- /dev/null +++ b/1-js/02-first-steps/08-operators/2-assignment-result/task.md @@ -0,0 +1,13 @@ +importance: 3 + +--- + +# 代入の結果 + +下のコードが実行されたあと、`a` と `x` はいくつになるでしょう? + +```js +let a = 2; + +let x = 1 + (a *= 2); +``` diff --git a/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/solution.md b/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/solution.md new file mode 100644 index 0000000000..d3e7ee2e8a --- /dev/null +++ b/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/solution.md @@ -0,0 +1,26 @@ + +```js no-beautify +"" + 1 + 0 = "10" // (1) +"" - 1 + 0 = -1 // (2) +true + false = 1 +6 / "3" = 2 +"2" * "3" = 6 +4 + 5 + "px" = "9px" +"$" + 4 + 5 = "$45" +"4" - 2 = 2 +"4px" - 2 = NaN +7 / 0 = Infinity +" -9 " + 5 = " -9 5" // (3) +" -9 " - 5 = -14 // (4) +null + 1 = 1 // (5) +undefined + 1 = NaN // (6) +" \t \n" - 2 = -2 // (7) +``` + +1. 文字列の追加 `"" + 1` では `1` を文字列に変換します: `"" + 1 = "1"`, そして `"1" + 0` には同じルールが適用されます。 +2. 減算 `-` (ほとんどの算術演算子と同様)は数値でのみ動作し、空の文字列 `""` を `0` に変換します +3. 文字列の追加は、数値 `5` を文字列に追加します。 +4. 減算は常に数値に変換します。そのため、`" -9 "` は数値 `-9` になります(前後のスペースは無視されます)。 +5. `null` は数値変換後は `0` になります。 +6. `undefined` は数値変換後は `NaN` になります。 +7. 文字列の先頭/末尾のスペースは、文字列が数値に変換される際に削除されます。ここでは文字列全体が `\t` や `\n`、"通常" のスペース文字から構成されています。したがって、空文字列のときと同様、`0` になります。 diff --git a/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/task.md b/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/task.md new file mode 100644 index 0000000000..4d25a1f207 --- /dev/null +++ b/1-js/02-first-steps/08-operators/3-primitive-conversions-questions/task.md @@ -0,0 +1,27 @@ +importance: 5 + +--- + +# 型変換 + +これらの式の結果はどうなるでしょう? + +```js no-beautify +"" + 1 + 0 +"" - 1 + 0 +true + false +6 / "3" +"2" * "3" +4 + 5 + "px" +"$" + 4 + 5 +"4" - 2 +"4px" - 2 +7 / 0 +" -9 " + 5 +" -9 " - 5 +null + 1 +undefined + 1 +" \t \n" - 2 +``` + +よく考え、書き留めてから答えあわせしてみてください。 diff --git a/1-js/02-first-steps/08-operators/4-fix-prompt/solution.md b/1-js/02-first-steps/08-operators/4-fix-prompt/solution.md new file mode 100644 index 0000000000..32cc581eaf --- /dev/null +++ b/1-js/02-first-steps/08-operators/4-fix-prompt/solution.md @@ -0,0 +1,32 @@ +理由はプロンプトがユーザ入力を文字列として返すからです。 + +なので、変数はそれぞれ値 `"1"` と `"2"` になります。 + +```js run +let a = "1"; // prompt("First number?", 1); +let b = "2"; // prompt("Second number?", 2); + +alert(a + b); // 12 +``` + +すべきことは、`+` の前に、文字列から数値へ変換することです。例えば、`Number()` を使用したり、それらの前に `+` をつけます。 + +例えば、。`prompt` の直前: + +```js run +let a = +prompt("First number?", 1); +let b = +prompt("Second number?", 2); + +alert(a + b); // 3 +``` + +あるいは `alert`: + +```js run +let a = prompt("First number?", 1); +let b = prompt("Second number?", 2); + +alert(+a + +b); // 3 +``` + +最新のコードでは、単項と二項の `+` 両方を使用しています。面白いですね。 diff --git a/1-js/02-first-steps/08-operators/4-fix-prompt/task.md b/1-js/02-first-steps/08-operators/4-fix-prompt/task.md new file mode 100644 index 0000000000..7809de4545 --- /dev/null +++ b/1-js/02-first-steps/08-operators/4-fix-prompt/task.md @@ -0,0 +1,18 @@ +importance: 5 + +--- + +# 足し算を修正する + +ユーザに2つの数字を訪ね、その合計を表示するコードがあります。 + +これは正しく機能していません。以下の例の出力は `12` です(デフォルトのプロンプトの値の場合)。 + +なぜでしょうか?修正してください。結果は `3` になるべきです。 + +```js run +let a = prompt("First number?", 1); +let b = prompt("Second number?", 2); + +alert(a + b); // 12 +``` diff --git a/1-js/02-first-steps/08-operators/article.md b/1-js/02-first-steps/08-operators/article.md new file mode 100644 index 0000000000..4b4a8fbaab --- /dev/null +++ b/1-js/02-first-steps/08-operators/article.md @@ -0,0 +1,475 @@ +# 演算子 + +多くの演算子は既に学校で学んでおり、よく知られています。加算 `+`, 乗算 `*`, 減算 `-` などです。 + +このチャプターでは、単純な演算子から初めて、次に学校の数学ではカバーされないJavaScript 固有の側面に集中していきます。 + +## 用語: "単項演算子"、 "二項演算子"、 "オペランド" + +次に進む前に一般的な用語を理解しましょう。 + +- *オペランド* -- は演算子が適用されるものです。たとえば、 乗算 `5 * 2` では、2つのオペランドがあります: 左のオペランドは `5`, 右のオペランドは `2` です。"オペランド" は "引数" と呼ばれることもあります。 +- 演算子が単一のオペランドをもつ場合は *単項演算* です。たとえば、負の単項演算 `"-"` は数値の符号を反転します: + + ```js run + let x = 1; + + *!* + x = -x; + */!* + alert( x ); // -1, 負の単項演算が適用されました + ``` +- 演算子が2つのオペランドを持つ場合は *二項演算* です。同じマイナスも二項演算で同様に存在します: + + ```js run no-beautify + let x = 1, y = 3; + alert( y - x ); // 2, 二項演算子マイナスは値を減算します + ``` + + 正式には、ここでは2つの異なる演算子について話をしています。: 負の単項演算(単一のオペランド, 符号の反転) と二項演算による減算(2つのオペランド、減算)です。 + +## Maths + +以下の算術演算子がサポートされています: + +- 加算 `+`, +- 減算 `-`, +- 乗算 `*`, +- 除算 `/`, +- 剰余 `%`, +- べき乗 `**`. + +最初の4つはそのままです。`%` と `**` は以下で補足します。 + +### 剰余 % + +剰余演算子 `%` は % という表記にも関わらずパーセントとは関係ありません。 + +`a % b` の結果は `a` を `b` で除算した[あまり](https://en.wikipedia.org/wiki/Remainder)です。 + +例: + +```js run +alert( 5 % 2 ); // 1, 5 / 2 のあまり +alert( 8 % 3 ); // 2, 8 / 3 のあまり +``` + +### べき乗 ** + +べき乗演算子 `a ** b` は `a` 自身を `b` 回かけます。 + +例: + +```js run +alert( 2 ** 2 ); // 4 (2 を 2 回) +alert( 2 ** 3 ); // 8 (2 * 2 * 2, 3 回) +alert( 2 ** 4 ); // 16 (2 * 2 * 2 * 2, 4 回) +``` + +数学的には、べき乗は非整数値も同様に定義されています。例えば、平方根は 1/2 によるべき乗です: + +```js run +alert( 4 ** (1/2) ); // 2 (1/2 の累乗は平方根と同じです) +alert( 8 ** (1/3) ); // 2 (1/3 の累乗は立方根と同じです) +``` + + +## 文字列の連結、二項演算子 + + +では、学校の算数を超えた JavaScript 演算子の特別な機能を見ていきましょう。 + +通常、プラス演算子 `+` は数値の合計です。 + +しかし二項演算子 `+` が文字列に適用された場合は、お互いの文字を結合します。 + +```js +let s = "my" + "string"; +alert(s); // mystring +``` + +一方のオペランドが文字列の場合、他のオペランドも文字列に変換されることに注意してください。 + +例: + +```js run +alert( '1' + 2 ); // "12" +alert( 2 + '1' ); // "21" +``` + +ご覧の通り、どちらのオペランドが文字列なのかは関係ありません。 + +こちらはより複雑な例です: + +```js run +alert(2 + 2 + '1' ); // "41" であり、"221" ではありません +``` + +ここで、演算子は順番に動作します。最初の `+` が2つの数値を合計し `4` を返します。次に、2つ目の `+` が文字列 `1` を足します。なので、`4 + '1' = '41'` のようになります。 + +```js run +alert('1' + 2 + 2); // "122" であり "14" ではありません +``` +ここで、最初のオペランドは文字列なので、コンパイラは他の2つのオペランドも文字列として扱います。`2` は `'1'` と連結するので、`'1' + 2 = "12"` となり、 `"12" + 2 = "122"` となります。 + +二項演算子の `+` はこのような方法で文字列をサポートする唯一の演算子です。他の算術演算子は数値でのみ動作し、常にオペランドを数値に変換します。 + +例えば、減算と除算です: + +```js run +alert( 6 - '2' ); // 4, '2' を数値に変換します +alert( '6' / '2' ); // 3, 両方のオペランドを数値に変換します +``` + +## 数値変換 単項演算子 + + +プラス `+` は2つの形で存在します。上で使ったような二項演算の形式と単項演算の形式です。 + +単項演算子プラス、もしくは言い換えると単一の値に適用されるプラス演算子 `+` は、数値に対しては何もしません。しかし、オペランドが数値でない場合は数値に変換します。 + +例: + +```js run +// 数値の場合、何の影響もありません +let x = 1; +alert( +x ); // 1 + +let y = -2; +alert( +y ); // -2 + +*!* +// 非数値を数値に変換します +alert( +true ); // 1 +alert( +"" ); // 0 +*/!* +``` + +これは `Number(...)` と同じですが、より短い表現です。 + +文字列から数値への変換が必要なケースは多いです。例えば、HTMLのフォームフィールドから値を取得する場合、それらは通常文字列です。今、それらの合計が欲しい場合はどうなるでしょう? + +二項演算子プラスはそれらを文字列として結合します。: + +```js run +let apples = "2"; +let oranges = "3"; + +alert( apples + oranges ); // "23", 二項演算子プラスは文字列を結合します +``` + +数値として扱いたい場合は変換して合計します: + +```js run +let apples = "2"; +let oranges = "3"; + +*!* +// 二項演算子プラスの処理の前に、両方の値が数値に変換されます +alert( +apples + +oranges ); // 5 +*/!* + +// 長い書き方 +// alert( Number(apples) + Number(oranges) ); // 5 +``` + +数学者の立場からは、余分なプラスは奇妙に見えるかもしれません。しかし、プログラマの立場からは特殊なことではありません: 単項演算子プラスが最初に適用され、文字列から数値に変換されます。次に二項演算子プラスはそれらを合計します。 + +なぜ二項演算子プラスの前に単項演算子プラスが適用されるのでしょうか? それは、単項演算子プラスの *優先順位が高いため* です。 + +## 演算子の優先順位 + +式が1つ以上の演算子をもつ場合、実行順はそれらの *優先順位* により決められます。言い換えると、演算子の間には暗黙の優先順があります。 + +学校で学んだように、式 `1 + 2 * 2` の場合、私たちは加算の前に乗算をすることを知っています。それがまさに優先順位です。乗算は加算より *より高い優先順位* です。 + +丸括弧はどの優先順位よりも優位に立つので、もとの優先順位が不適当であれば丸括弧を使います。たとえば `(1 + 2) * 2` のようにです。 + +JavaScriptでは多くの演算子があります。どの演算子も対応する優先順位を持っています。より大きな値をもつ演算子は最初に実行されます。同じ優先順の場合、実行順は左から右になります。 + +[優先順位テーブル](https://developer.mozilla.org/en/JavaScript/Reference/operators/operator_precedence)の抜粋(これを覚えておく必要はありませんが、単項演算子は対応する二項演算子よりも優先順位が高いことに留意してください): + +| 優先順位 | 名前 | 符号 | +|------------|------|------| +| ... | ... | ... | +| 15 | 単項プラス | `+` | +| 15 | 単項否定 | `-` | +| 14 | 冪乗 | `**` | +| 13 | 乗算 | `*` | +| 13 | 除算 | `/` | +| 12 | 加算 | `+` | +| 12 | 減算 | `-` | +| ... | ... | ... | +| 2 | 代入 | `=` | +| ... | ... | ... | + +ご覧の通り、 "単項演算子プラス" は優先順位 `15` で、 "加算" の `12` よりも大きいです(二項演算子プラス)。なので、式 `"+apples + +oranges"` において、単項演算子プラスは最初に動作し、次に加算が実行されます。 + +## 代入 + +代入 `=` もまた演算子であることに注意しましょう。 `2`というとても低い値として優先順位の一覧に並んでいます。 + +なので `x = 2 * 2 + 1` のように変数に代入するとき、計算が最初に行われ、その後 `=` が評価され、 `x` に結果が格納されます。 + +```js +let x = 2 * 2 + 1; + +alert( x ); // 5 +``` + +### 代入 = は値を返します + +`=` が演算子であり、"魔法の" 言語構造でないという事実は、興味深い意味合いを持っています。 + +JavaScript のすべての演算子は値を返却します。これは `+` や `-` では明らかですが、`=` の場合にも当てはまります。 + +`x = value` の呼び出しでは、`value` を `x` に書き込み、*その値を返却します。* + +これは、複雑な式の一部に代入を使用した例です: + +```js run +let a = 1; +let b = 2; + +*!* +let c = 3 - (a = b + 1); +*/!* + +alert( a ); // 3 +alert( c ); // 0 +``` + +上記の例では、式 `(a = b + 1)` の結果は `a` に代入された値(つまり `3`)です。その後、以降の評価で利用されています。 + +面白いですよね? JavaScript ライブラリで時々目にするので、これがどのように動くのかは理解しておく必要があります。 + +ですが、このようなコードは書かないでください。このようなトリッキーな書き方がコードの動きを明確にしたり、可読性をあげることはありません。 + +# 代入のチェーン + +もう1つの興味深い特徴は、代入をチェーンする機能です: + +```js run +let a, b, c; + +*!* +a = b = c = 2 + 2; +*/!* + +alert( a ); // 4 +alert( b ); // 4 +alert( c ); // 4 +``` + +チェーンされた代入は右から左へ評価されます。最初に最も右の式 `2 + 2` が評価され、次に左の変数に代入されます。: `c`, `b` と `a`です。最後にすべての変数は単一の値になります。 + +改めて言いますが、可読性を上げるためには、このようなコードを複数行に分割する方がよいです: + +```js +c = 2 + 2; +b = c; +a = c; +``` +これは読みやすいですね。コードを素早く眺めているときには特に。 + +## インプレース(in-place)修正 + +変数に演算子を適用したあと、新しい結果を同じ変数に格納したいことは頻繁にあります。 + +例: + +```js +let n = 2; +n = n + 5; +n = n * 2; +``` + +この表記は演算子 `+=` や `*=` を使用して短縮することができます: + +```js run +let n = 2; +n += 5; // n = 7 (n = n + 5 と同じ) +n *= 2; // n = 14 (n = n * 2 と同じ) + +alert( n ); // 14 +``` + +短縮の "変更と代入" 演算子はすべての算術演算とビット演算子に存在します: `/=`, `-=` 等 + +このような演算子は通常の代入と同じ優先順位になります。なので、他のほとんどの計算の後に実行されます: + +```js run +let n = 2; + +n *= 3 + 5; + +alert( n ); // 16 (最初に右辺が評価されるので n *= 8 と同じです) +``` + +## インクリメント/デクリメント + + + +数値を1ずつ増減する操作は、最も一般的な数値演算の1つです。 + +なので、そのための特別な演算子があります: + +- **インクリメント** `++` 変数を1増加させる: + + ```js run no-beautify + let counter = 2; + counter++; // counter = counter + 1 と同じですがより短いです + alert( counter ); // 3 + ``` +- **デクリメント** `--` 変数を1減少させる: + + ```js run no-beautify + let counter = 2; + counter--; // counter = counter - 1 と同じですがより短いです + alert( counter ); // 1 + ``` + +```warn +インクリメント/デクリメントは変数に対してのみ適用可能です。 それを `5++` のように値に対して使おうとするとエラーになります。 +``` + +演算子 `++` と `--` は変数の前後両方に配置することができます。 + +- 演算子が変数の後にある場合、それは "後置式" と呼ばれます: `counter++`。 +- "前置式" は演算子が変数の前に来るときです: `++counter`。 + +結果はどちらも同じです: `counter` を `1` 増加します。 + +それらに違いはあるでしょうか?はい、その違いは `++/--` の戻り値を使う場合にだけ現れます。 + +違いを明確にしましょう。ご存知の通り、すべての演算子は値を返します。インクリメント/デクリメントも例外ではありません。前置式は新しい値を返す一方、後置式は古い値を返します(インクリメント/デクリメントの前)。 + +違いを例で見てみましょう。 + +```js run +let counter = 1; +let a = ++counter; // (*) + +alert(a); // *!*2*/!* +``` + +ここで `(*)` の行の前置呼び出し `++counter` は `counter` を増加させ、`2` という新しい値を返します。そのため、 `alert` は `2` を表示します。 + +後置式を使いましょう: + +```js run +let counter = 1; +let a = counter++; // (*) ++counter を counter++ に変更 + +alert(a); // *!*1*/!* +``` + +`(*)` の行で、 *後置* 式 `counter++` は `counter` を増加させますが、 *古い* 値を返します(増加する前)。そのため、 `alert` は `1` を表示します。 + +要約すると: + +- インクリメント/デクリメントの結果を使わない場合、どちらの形式を使っても違いはありません。: + + ```js run + let counter = 0; + counter++; + ++counter; + alert( counter ); // 2, 上の行は同じことをします + ``` +- 値の増加に *加えて*、すぐに演算子の結果を使いたい場合は前置式が必要になります: + + ```js run + let counter = 0; + alert( ++counter ); // 1 + ``` +- 増加させるが、以前の値を使いたい場合は後置式が必要です: + + ```js run + let counter = 0; + alert( counter++ ); // 0 + ``` + +````smart header="他の演算子の中でのインクリメント/デクリメント" +演算子 `++/--` は同様に式の中でも使うことができます。それらの優先順位は他の算術演算子よりも高いです。 + +例: + +```js run +let counter = 1; +alert( 2 * ++counter ); // 4 +``` + +比較: + +```js run +let counter = 1; +alert( 2 * counter++ ); // 2, counter++ は "古い" 値を返すからです +``` + +技術的には問題ありませんが、このような記法は一般的にコードの可読性を下げます。1行で複数のことを行う -- よいことではありません。 + +コードを読むとき、上から読んでいく "縦の" 目視はこのような `counter++` を見逃しやすく、また変数の増加が明白ではありません。 + +"1行は1アクション" のスタイルが推奨されます: + +```js run +let counter = 1; +alert( 2 * counter ); +counter++; +``` +```` + +## ビット演算子 + +ビット演算子は引数を 32ビットの整数値として扱い、それらのバイナリ表現のレベルで処理します。 + +これらの演算子はJavaScript固有のものではありません。多くのプログラミング言語でサポートされています。 + +演算子のリスト: + +- AND ( `&` ) +- OR ( `|` ) +- XOR ( `^` ) +- NOT ( `~` ) +- LEFT SHIFT ( `<<` ) +- RIGHT SHIFT ( `>>` ) +- ZERO-FILL RIGHT SHIFT ( `>>>` ) + +これらの演算子はめったに使われません。それらを理解するためには、低レベルの数値表現について掘り下げるべきであり、それは現時点では最適ではないでしょう。すぐには必要ないからです。もし興味がある場合は、MDNの[ビット演算子 ](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators#binary_bitwise_operators)の記事を参照してください。実際に必要になったときにそれをするのが現実的でしょう。 + +## カンマ + +カンマ演算子 `','` は最もレアで普通ではない演算子の1つです。より短いコード書くために使われることがあるので、何が起こっているのか理解するために知っておく必要があります。 + +カンマ演算子を使うと複数の式を評価できます。それらの式はカンマ `','` で区切られています。それぞれが評価されますが、最後の結果のみが返却されます。 + +例: + +```js run +*!* +let a = (1 + 2, 3 + 4); +*/!* + +alert( a ); // 7 (3 + 4 の結果) +``` + +ここで、最初の式 `1 + 2` は評価され、その結果はどこかへ捨てられます。次に `3 + 4` が評価され、結果として返却されます。 + +```smart header="カンマはとても優先順が低いです" +カンマ演算子はとても優先順位が低いことに注意してください。 `=` よりも低いため、上の例では丸括弧が重要です。 + +それらがない場合: `a = 1 + 2, 3 + 4` は `+` を最初に評価し、数値を `a = 3, 7` に加算します。次に代入演算子 `=` が `a = 3` を割り当てます。そして、カンマのあとの `7` は処理されず、無視されます。 +``` + +なぜこのような、最後の部分を除いてすべてを捨てる演算子が必要なのでしょうか? + +より複雑な構造において、1行で複数のアクションを書くときに使用される場合があります。 + +例: + +```js +// 1行に3つの演算子 +for (*!*a = 1, b = 3, c = a * b*/!*; a < 10; a++) { + ... +} +``` + +このようなトリックは多くのJavaScriptフレームワークで利用されているため、ここで言及しています。しかし通常それらはコードの可読性を下げます。なので、そのように書く前によく考えるべきです。 diff --git a/1-js/02-first-steps/09-alert-prompt-confirm/article.md b/1-js/02-first-steps/09-alert-prompt-confirm/article.md deleted file mode 100644 index 7c17a573d5..0000000000 --- a/1-js/02-first-steps/09-alert-prompt-confirm/article.md +++ /dev/null @@ -1,111 +0,0 @@ -# 対話: alert, prompt, confirm - -チュートリアルのこのパートは、環境依存なしで、JavaScript "そのまま" を説明することを目的としています。 - -しかし、私たちはデモ環境としてブラウザを使っているので、少なくともいくつかのユーザインタフェース機能について知っておく必要があります。このチャプターでは、ブラウザ機能である `alert`, `prompt` そして `confirm` について親しみます。 - -[cut] - -## alert - -構文: - -```js -alert(message); -``` - -これはメッセージを表示し、ユーザが "OK" を押すまでスクリプトの実行を停止します。 - -例: - -```js run -alert("Hello"); -``` - -メッセージのある小さいウィンドウは *モーダルウィンドウ* と呼ばれます。"モーダル" という言葉は、訪問者はページの他の部分と対話したり、他のボタンを押すことができないことを意味します -- 彼らがそのウィンドウを扱うまで。この場合、 -- 彼らが "OK" を押すまで。 - -## prompt - -`prompt` 機能は2つの引数を受け入れます: - -```js no-beautify -result = prompt(title[, default]); -``` - -テキストメッセージ、訪問者のための入力フィールド、OK/CANCEL ボタンをもつ小窓を表示します。 - -`title` -: 訪問者へ表示するテキストです。 - -`default` -: 任意の2つ目のパラメータで、入力フィールドの初期値です。 - -訪問者はプロンプトの入力フィールドに何か入力し、OKを押すかもしれません。または CANCEL ボタンの押下、もしくは `key:Esc` キーにより入力をキャンセルすることができます。 - -`prompt` の呼び出しはフィールドのテキストもしくは、入力がキャンセルされた場合は `null` が返却されます。 - -例: - -```js run -let age = prompt('How old are you?', 100); - -alert(`You are ${age} years old!`); // You are 100 years old! -``` - -````warn header="IE: 常に `デフォルト` を提供してください" -2つ目のパラメータは任意です。しかし、それを供給しない場合、Internet Explorer はプロントにテキスト `"undefined"` を挿入します。 - -それを見るには Internet Explorer でこのコードを実行しましょう: - -```js run -let test = prompt("Test"); -``` - -なので IEで良く見るためには、常に2つ目の引数を指定することが推奨されます。: - -```js run -let test = prompt("Test", ''); // <-- for IE -``` -```` - -## confirm - -構文: - -```js -result = confirm(question); -``` - -`confirm` 関数は `question` と 2つのボタンをもつモーダルウィンドウを表示します。: OK と キャンセル - -OK が押された場合の結果は `true` で、それ以外は `false` です。 - -例: - -```js run -let isBoss = confirm("Are you the boss?"); - -alert( isBoss ); // true OKが押された場合 -``` - -## サマリ - -私たちは、訪問者とやり取りをするための3つのブラウザ固有の関数を説明しました。 - -`alert` -: メッセージを表示します - -`prompt` -: ユーザにテキスト入力を求めるメッセージを表示します。テキスト、もしくはCANCELまたは `key:Esc` がクリックされた場合、全てのブラウザは `null` を返します。 - -`confirm` -: メッセージを表示し、ユーザが "OK" または "CANCEL" を押すのを待ちます。OKの場合は `true` を、CANCEL/`key:Esc` の場合は `false` を返します。 - -これらすべてのメソッドはモーダルです: スクリプトの実行を中断し、メッセージが消えるまで訪問者がページの他の部分とやり取りするのを禁止します。 - -上のすべてのメソッドで共有される、2つの制限があります。: - -1. モーダルウィンドウの正確な位置はブラウザによって決定されます。通常それは中央です。 -2. ウィンドウの正確な見た目もまたブラウザに依存し、それを修正することはできません。 - -それは単純化に対する代償です。より良いウィンドウを表示し、訪問者とのよりリッチなインタラクションを実現する方法もありますが、"不要な装飾" があまり重要でない場合、これらのメソッドが使えます。 diff --git a/1-js/02-first-steps/08-comparison/1-comparison-questions/solution.md b/1-js/02-first-steps/09-comparison/1-comparison-questions/solution.md similarity index 100% rename from 1-js/02-first-steps/08-comparison/1-comparison-questions/solution.md rename to 1-js/02-first-steps/09-comparison/1-comparison-questions/solution.md diff --git a/1-js/02-first-steps/08-comparison/1-comparison-questions/task.md b/1-js/02-first-steps/09-comparison/1-comparison-questions/task.md similarity index 100% rename from 1-js/02-first-steps/08-comparison/1-comparison-questions/task.md rename to 1-js/02-first-steps/09-comparison/1-comparison-questions/task.md diff --git a/1-js/02-first-steps/09-comparison/article.md b/1-js/02-first-steps/09-comparison/article.md new file mode 100644 index 0000000000..7d73111424 --- /dev/null +++ b/1-js/02-first-steps/09-comparison/article.md @@ -0,0 +1,216 @@ +# 比較 + +私たちは数学にある多くの比較演算子を知っています。: + +JavaScript では、次のような記述します: + +- より大きい/より小さい: a > b, a < b +- 大きい/小さいまたは等しい: a >= b, a <= b. +- 等位チェックは `a == b` として書かれます(2重の等式記号 `'='` に注意してください。1つの記号 `a = b` は代入を意味します。) +- 等しくない。数学において、この記法は です。JavaScriptにおいては、感嘆符がその前についた代入として書かれます: a != b. + +この記事では、異なる比較のタイプに関して、JavaScript ではどうやるか、またその重要な特性について詳しく学んでいきます。 + +記事の末尾には "JavaScriptの癖" に関連する問題を回避するための良いレシピがあります。 + +## Boolean は結果です + +すべての比較演算子はブール値を返します: + +- `true` -- "はい", "正しい" もしくは "真" を意味します. +- `false` -- "いいえ", "誤り" もしくは "偽" を意味します. + +例: + +```js run +alert( 2 > 1 ); // true (正しい) +alert( 2 == 1 ); // false (誤り) +alert( 2 != 1 ); // true (正しい) +``` + +任意の値のように、比較結果は変数に代入することができます: + +```js run +let result = 5 > 4; // 比較の結果を代入 +alert( result ); // true +``` + +## 文字列比較 + +どちらの文字列がより大きいかを見る場合、いわゆる "辞書" もしくは "数学における辞書式順序" の順序が使われます。 + +言い換えると、文字列は文字単位で比較されます。 + +例: + +```js run +alert( 'Z' > 'A' ); // true +alert( 'Glow' > 'Glee' ); // true +alert( 'Bee' > 'Be' ); // true +``` + +2つの文字列を比較するアルゴリズムはシンプルです: + +1. 両方の文字列の最初の文字を比較します。 +2. 1つ目のほうが大きい(もしくは小さい)場合、1つ目の文字列は2つ目の文字列よりも大きい(もしくは小さい)です。それで完了です。 +3. そうではなく、もしも最初の文字が等しい場合は、同じ方法で2つ目の文字を比較します。 +4. 文字列の最後までそれを繰り返します。 +5. 両方の文字列が同時に終わった場合、それらは等しいです。そうでなければ長い文字列がより大きいです。 + +上の例において、比較 `'Z' > 'A'` は最初のステップで結果を得ます。 + +文字列 `"Glow"` と `"Glee"` は文字単位の比較がされます。 + +1. `G` は `G` と同じ. +2. `l` は `l` と同じ. +3. `o` は `e` より大きい. ここでストップ. 1つ目の文字列のほうが大きいです. + +```smart header="実際の辞書ではなく、Unicode順です" +上で与えられている比較アルゴリズムは、本の辞書や電話帳で使われているのとおおよそ同じです。しかし全く同じではありません。 + +例えば、大文字小文字の問題。大文字 `"A"` は小文字の `"a"` とは同じではありません。どちらがより大きいでしょう?実際は、小文字 `"a"` です。なぜでしょう?なぜなら、小文字は内部のエンコーディングテーブル(Unicode)でより大きな値を持っているからです。チャプター で具体的な詳細と結果を取り上げます。 +``` + +## 異なる型の比較 + +異なる型に所属している値を比較するとき、それらは数値に変換されます。 + +例: + +```js run +alert( '2' > 1 ); // true, 文字列 '2' は数値 2 になります +alert( '01' == 1 ); // true, 文字列 '01' は数値 1 になります +``` + +真偽値の場合、`true` は `1` になり、 `false` は `0` になります。: + +例: + +```js run +alert( true == 1 ); // true +alert( false == 0 ); // true +``` + +````smart header="興味深い結果" +次の2つが同時に発生する場合があります: + +- 2つの値が等しい +- それらの一方は真偽値の `true` で、もう一方は真偽値の `false` + +例: + +```js run +let a = 0; +alert( Boolean(a) ); // false + +let b = "0"; +alert( Boolean(b) ); // true + +alert(a == b); // true! +``` + +JavaScriptの立場からすると、それは普通です。等価チェックは数値変換を使って変換をします(したがって、`"0"` は `0` になります)。一方、 明示的な `Boolean` 変換は別のルールセットを利用します。 +```` + +## 厳密な等価 + +通常の等価チェック `"=="` は問題を持っています。`0` と `false` を異なるものと判断させることはできません: + +```js run +alert( 0 == false ); // true +``` + +空文字列でも同じです: + +```js run +alert( '' == false ); // true +``` + +これは、異なる型のオペランドは等価演算子 `==` によって数値に変換されるためです。空文字は、ちょうど `false` のように 0 になります。 + +もしも `0` と `false` を分けたい場合、どうすべきでしょうか? + +**厳密等価演算子 `===` は型変換なしで等価をチェックします。** + +言い換えると、もしも `a` と `b` が異なる型の場合、`a === b` はそれらの変換の試みをすることなく、すぐに `false` を返します。 + +試してみましょう: + +```js run +alert( 0 === false ); // false, 型が異なるためです +``` + +`!=` の類似として、"厳密な非等価" 演算子 `!==` も存在します。 + +厳密等価チェック演算子は書くのが少し長いですが、起こっていることを明らかにし、エラーの余地を少なくします。 + +## null と undefined の比較 + +`null` もしくは `undefined` が他の値と比較される場合、非直感的な振る舞いになります。 + +厳密な等価チェック `===` の場合 +: それぞれが自身の別々の型に所属しているため、これらの値は異なります。 + + ```js run + alert( null === undefined ); // false + ``` + +非厳密なチェック `==` の場合 +: 特別なルールがあります。この2つは "スイートカップル" と呼ばれ、(`==` の意味で)等しくなりますが、これら以外の値とは等しいと扱われることはありません。 + + ```js run + alert( null == undefined ); // true + ``` + +数学や他の比較 `< > <= >=` +: 値 `null/undefined` は数値に変換されます: `null` は `0` になり、`undefined` は `NaN` (Not a Number)になります。 + +今、それらのルールを適用した時に起こる面白いことを見てみましょう。そして、より重要なことはこれらの機能でトラップに陥らない方法です。 + +### 奇妙な結果: null vs 0 + +`null` とゼロを比較してみましょう: + +```js run +alert( null > 0 ); // (1) false +alert( null == 0 ); // (2) false +alert( null >= 0 ); // (3) *!*true*/!* +``` + +上の3つの例は数学的には奇妙です。最後の結果は "`null` はゼロより大きいまたは等しい" ことを述べています。そうであれば上2つの比較のどちらかは正しくなければいけませんが、両方とも false です。 + +その理由は等価チェック `==` と比較 `> < >= <=` は異なった処理するためです。比較は `null` を数値に変換します、したがって `0` として扱います。そういう訳で (3) `null >= 0` は true で、 (1) は false になります。 + +一方、`undefined` と `null` の等価チェック `==` はいずれの変換もなしにルールによって処理します。それらはお互い等価で他のいずれとも等価ではありません。なので (2) `null == 0` は false です。 + +### 比べるものがない undefined + +値 `undefined` は比較に関与しません。: + +```js run +alert( undefined > 0 ); // false (1) +alert( undefined < 0 ); // false (2) +alert( undefined == 0 ); // false (3) +``` + +なぜそこまでゼロが嫌いなのでしょう?常に false です! + +このような結果になった理由は次の通りです: + +- 比較 `(1)` と `(2)` は、 `undefined` は `NaN` に変換されるため `false` を返します。また、`NaN` はすべての比較で `false` を返す特別な数値です。 +- `undefined` は `null` とのみ等価で、それ以外とは等価ではないため、等価チェック `(3)` は `false` を返します。 + +### 問題を回避する + +なぜこれらの例を見てきたのでしょう?常にこれらの特殊性を覚えておく必要があるでしょうか?そうではありません。実際には、これらのトリッキーなことは徐々に馴染みの深いものになっていくでしょう。ですが、問題を回避するための確実な方法があります。 + +- 厳密な等価 `===` 以外の比較演算子については、例外的な注意を払って `undefined/null` の比較を行ってください。 +- 本当に正しい場合でない限り、`null/undefined` かもしれない変数に対して比較 `>= > < <=` は使ってはいけません。変数がこのような値を持つ可能性がある場合は、それらを別々にチェックしてください。 + +## サマリ + +- 比較演算子は論理値を返します。 +- 文字列は "辞書” 順で、1文字ずつ比較されます。 +- 異なった型の値が比較される場合、それらは数値に変換されます(厳密な等価チェックを除く) +- 値 `null` と `undefined` はそれぞれ等価 `==` であり、それ以外の値とは等価ではありません。 +- `>` または `<` のような比較を、`null/undefined` になる可能性のある変数に対して使う場合は注意してください。`null/undefined` を別々にチェックするのが良いアイデアです。 diff --git a/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2.png b/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2.png deleted file mode 100644 index 8c57b18850..0000000000 Binary files a/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2.png and /dev/null differ diff --git a/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2.svg b/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2.svg new file mode 100644 index 0000000000..47b020aab1 --- /dev/null +++ b/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2.svg @@ -0,0 +1 @@ +BeginYou don't know? “ECMAScript”!Right!What's the “official” name of JavaScript?OtherECMAScript \ No newline at end of file diff --git a/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2@2x.png b/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2@2x.png deleted file mode 100644 index cbb2c611a0..0000000000 Binary files a/1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2@2x.png and /dev/null differ diff --git a/1-js/02-first-steps/10-ifelse/2-check-standard/task.md b/1-js/02-first-steps/10-ifelse/2-check-standard/task.md index bd1c65ddfe..401a857ae5 100644 --- a/1-js/02-first-steps/10-ifelse/2-check-standard/task.md +++ b/1-js/02-first-steps/10-ifelse/2-check-standard/task.md @@ -8,6 +8,6 @@ importance: 2 もし、訪問者が "ECMAScript" と入力したら、 "Right!" を出力し、それ以外は -- "Didn't know? ECMAScript!" と出力します。 -![](ifelse_task2.png) +![](ifelse_task2.svg) [demo src="ifelse_task2"] diff --git a/1-js/02-first-steps/10-ifelse/4-check-login/ifelse_task.png b/1-js/02-first-steps/10-ifelse/4-check-login/ifelse_task.png deleted file mode 100644 index 8b54dc83d5..0000000000 Binary files a/1-js/02-first-steps/10-ifelse/4-check-login/ifelse_task.png and /dev/null differ diff --git a/1-js/02-first-steps/10-ifelse/4-check-login/ifelse_task@2x.png b/1-js/02-first-steps/10-ifelse/4-check-login/ifelse_task@2x.png deleted file mode 100644 index 92001dfe86..0000000000 Binary files a/1-js/02-first-steps/10-ifelse/4-check-login/ifelse_task@2x.png and /dev/null differ diff --git a/1-js/02-first-steps/10-ifelse/4-check-login/solution.md b/1-js/02-first-steps/10-ifelse/4-check-login/solution.md deleted file mode 100644 index 7de900619a..0000000000 --- a/1-js/02-first-steps/10-ifelse/4-check-login/solution.md +++ /dev/null @@ -1,25 +0,0 @@ - - -```js run demo -let userName = prompt("Who's there?", ''); - -if (userName == 'Admin') { - - let pass = prompt('Password?', ''); - - if (pass == 'TheMaster') { - alert( 'Welcome!' ); - } else if (pass == null) { - alert( 'Canceled.' ); - } else { - alert( 'Wrong password' ); - } - -} else if (userName == null) { - alert( 'Canceled' ); -} else { - alert( "I don't know you" ); -} -``` - -`if` ブロック内の縦のインデントに注意してください。技術的には必須ではありませんが、コードの可読性をより良くします。 diff --git a/1-js/02-first-steps/10-ifelse/4-check-login/task.md b/1-js/02-first-steps/10-ifelse/4-check-login/task.md deleted file mode 100644 index 0ba1a48cc9..0000000000 --- a/1-js/02-first-steps/10-ifelse/4-check-login/task.md +++ /dev/null @@ -1,24 +0,0 @@ -importance: 3 - ---- - -# ログインのチェック - -`prompt` でログインを要求するコードを書いてください。 - -もし訪問者が `"Admin"` と入力したら、パスワードのための `prompt` を出します。もし入力が空行または `key:Esc` の場合 -- "Canceled" と表示します。別の文字列の場合は -- "I don't know you" と表示します。 - -パスワードは次に沿ってチェックされます: - -- ”TheMaster" と等しい場合には "Welcome!" と表示します。 -- 別の文字列の場合 -- "Wrong password" を表示します。 -- 空文字または入力がキャンセルされた場合には "Canceled." と表示します。 - - -図: - -![](ifelse_task.png) - -入れ子の `if` ブロックを使ってください。コードの全体的な読みやすさに気をつけてください。 - -[demo] diff --git a/1-js/02-first-steps/10-ifelse/article.md b/1-js/02-first-steps/10-ifelse/article.md index 7ea62a5484..b58c8ff7ea 100644 --- a/1-js/02-first-steps/10-ifelse/article.md +++ b/1-js/02-first-steps/10-ifelse/article.md @@ -1,10 +1,8 @@ -# 条件演算子: if, '?' +# 条件分岐: if, '?' 時には、条件に基づき異なるアクションを実行する必要があります。 -そのための `if` 文と、簡単にするための "疑問符" 演算子 `"?"` と呼ばれる条件付き評価のための条件付き(3項)演算子があります。 - -[cut] +そのための `if` 文と、 "疑問符" 演算子とも呼ばれる条件付き演算子(3項演算子) `"?"` があります。 ## "if" 文 @@ -20,7 +18,7 @@ if (year == 2015) alert( 'You are right!' ); */!* ``` -上の例では、条件はシンプルな等価チェックです: `year == 2015`。より複雑にすることもできます。 +上の例は、シンプルな等価チェック(`year == 2015`)ですが、より複雑にすることもできます。 実行する文が複数ある場合、コードブロックを波括弧で囲む必要があります。: @@ -31,7 +29,7 @@ if (year == 2015) { } ``` -たとえ1つの文しかない場合でも `if` を使用するときは波括弧でコードブロックを囲むことを推奨します。これは可読性を向上させます。 +たとえ1つの文しかない場合でも `if` を使用するときは波括弧 `{}` でコードブロックを囲むことを推奨します。これは可読性を向上させます。 ## Boolean 変換 @@ -50,7 +48,7 @@ if (0) { // 0 は偽 } ``` -...また、この条件は -- 常に処理されます: +また、この条件は -- 常に処理されます: ```js if (1) { // 1 は真 @@ -70,7 +68,7 @@ if (cond) { ## "else" 句 -`if` 文は任意の "else" ブロックを持っているかもしれません。それは条件が間違っている場合に実行します。 +`if` 文は任意の "else" ブロックを持つ場合があり、それは条件が偽の場合に実行されます。 例: ```js run @@ -85,7 +83,7 @@ if (year == 2015) { ## いくつかの条件: "else if" -私たちはいくつかの条件のパターンをテストしたい時があります。そのために `else if` 句があります。 +いくつかの条件のパターンをテストしたい時があります。そのために `else if` 句があります。 例: @@ -101,13 +99,13 @@ if (year < 2015) { } ``` -上のコードで、JavaScriptは最初に `year < 2015` をチェックします。もしもそれが偽の場合、次の条件 `year > 2015` に行きます。それでもない場合は、最後の `alert` を表示します。 +上のコードで、JavaScriptは最初に `year < 2015` をチェックします。それが偽の場合、次の条件 `year > 2015` の判定を行います。それもまた偽の場合、最後の `alert` を表示します。 -多くの `else if` ブロックを持つことができます。最後の `else` は任意です。 +複数の `else if` ブロックを持つことができます。最後の `else` は任意です。 ## 3項演算子 '?' -条件に依存して変数に代入する必要がある場合があります。 +条件に依存して変数へ代入を行う必要がある場合があります。 例: @@ -126,9 +124,9 @@ if (age > 18) { alert(accessAllowed); ``` -いわゆる、"3項" もしくは "疑問符" 演算子は、より短く簡単に行うことができます。 +いわゆる、"条件付き" もしくは "疑問符" 演算子では、より短く簡単に行うことができます。 -演算子は疑問符 `"?"` で表されます。公式な用語 "3項" は演算子は3つのオペランドを持つことを意味します。 +演算子は疑問符 `"?"` で表されます。演算子が3つのオペランドを持つことから、 "三項演算子" と呼ばれることもあります。これは、JavaScriptの中で3つのオペランドを持つ唯一の演算子です。 構文は次の通りです: ```js @@ -143,7 +141,9 @@ let result = condition ? value1 : value2 let accessAllowed = (age > 18) ? true : false; ``` -技術的には、私たちは `age > 18` の周りの括弧を省くことができます。疑問符演算子は低い優先順位を持っているので、比較 `>` の後に実行されます。そのためそれらは同じように動作します: +技術的には、`age > 18` の周りの括弧を省くことができます。疑問符演算子は低い優先順位を持っているので、比較 `>` の後に実行されます。 + +以下の例は上の例と同じように動作します: ```js // 比較演算子 "age > 18" が最初に実行されます @@ -151,7 +151,7 @@ let accessAllowed = (age > 18) ? true : false; let accessAllowed = age > 18 ? true : false; ``` -...しかし、括弧はコードの可読性をより良くします。そのため、括弧を使うことが推奨されます。 +しかし、括弧はコードの可読性をより良くします。そのため、括弧を使うことが推奨されます。 ````smart 上の例では、比較自体が `true/false` を返すため、疑問符演算子を回避することが可能です。 @@ -181,9 +181,9 @@ alert( message ); 最初、それが何をしているのか掴むのが難しいかもしれません。しかしよく見るとそれがただの通常の一連のテストであることがわかります。 1. 最初の疑問符は `age < 3` かどうかチェックします。 -2. もしも真の場合 -- `'Hi, baby!'` を返します。そうでなければ -- コロン `":"` の後に行き、`age < 18` をチェックします。 -3. もしもそれが真であれば -- `'Hello!'` を返します。そうでなければ -- コロン `":"` の後に行き、`age < 100` をチェックします。 -4. もしもそれが真であれば -- `'Greetings!'` を返します。そうでなければ -- コロン `":"` の後に行き、`What an unusual age` を返します。 +2. 真の場合 -- `'Hi, baby!'` を返します。そうでなければ -- コロン `":"` の後に行き、`age < 18` をチェックします。 +3. それが真であれば -- `'Hello!'` を返します。そうでなければ -- コロン `":"` の後に行き、`age < 100` をチェックします。 +4. それが真であれば -- `'Greetings!'` を返します。そうでなければ -- コロン `":"` の後に行き、`What an unusual age` を返します。 `if..else` を使った同じロジックです: @@ -214,7 +214,7 @@ let company = prompt('Which company created JavaScript?', ''); 条件 `company == 'Netscape'` に応じて、`"?"` の後の1つ目もしくは2つ目の部分が実行されアラートが表示されます。 -ここでは変数に結果を代入していません。この考えは、条件に応じて異なるコードを実行させるものです。 +ここでは変数に結果を代入していません。このアイデアは条件に応じて異なるコードを実行させるものです。 **このような方法で疑問符演算子を使うことは推奨されていません。** @@ -234,7 +234,6 @@ if (company == 'Netscape') { */!* ``` -私たちの目はコードを縦にスキャンします。複数行にまたがる構造は、長い水平な命令セットよりも理解しやすいです。 +私たちの目はコードを縦に見ていきます。複数行にまたがる構造は、長い水平な命令セットよりも理解しやすいです。 -疑問符 `'?'` の考え方は、条件によって別の値を返すことです。まさにそのために使ってください。 -異なるコードの枝葉を実行するために `if` があります。 +疑問符 `'?'` の目的は、条件によって別の値を返すことです。まさにそのために使ってください。異なるコードの枝葉を実行するために `if` があります。 diff --git a/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task.svg b/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task.svg new file mode 100644 index 0000000000..d22b518a91 --- /dev/null +++ b/1-js/02-first-steps/11-logical-operators/9-check-login/ifelse_task.svg @@ -0,0 +1 @@ +BeginCanceledCanceledWelcome!I don't know youWrong passwordWho's there?Password?CancelCancelAdminTheMasterOtherOther \ No newline at end of file diff --git a/1-js/02-first-steps/11-logical-operators/9-check-login/solution.md b/1-js/02-first-steps/11-logical-operators/9-check-login/solution.md new file mode 100644 index 0000000000..b46558f985 --- /dev/null +++ b/1-js/02-first-steps/11-logical-operators/9-check-login/solution.md @@ -0,0 +1,25 @@ + + +```js run demo +let userName = prompt("Who's there?", ''); + +if (userName === 'Admin') { + + let pass = prompt('Password?', ''); + + if (pass === 'TheMaster') { + alert( 'Welcome!' ); + } else if (pass === '' || pass === null) { + alert( 'Canceled' ); + } else { + alert( 'Wrong password' ); + } + +} else if (userName === '' || userName === null) { + alert( 'Canceled' ); +} else { + alert( "I don't know you" ); +} +``` + +`if` ブロック内の縦のインデントに注意してください。技術的には必須ではありませんが、コードの可読性をより良くします。 diff --git a/1-js/02-first-steps/11-logical-operators/9-check-login/task.md b/1-js/02-first-steps/11-logical-operators/9-check-login/task.md new file mode 100644 index 0000000000..bcedacb062 --- /dev/null +++ b/1-js/02-first-steps/11-logical-operators/9-check-login/task.md @@ -0,0 +1,24 @@ +importance: 3 + +--- + +# ログインのチェック + +`prompt` でログインを要求するコードを書いてください。 + +もし訪問者が `"Admin"` と入力したら、パスワードのための `prompt` を出します。もし入力が空行または `key:Esc` の場合 -- "Canceled" と表示します。別の文字列の場合は -- "I don't know you" と表示します。 + +パスワードは次に沿ってチェックされます: + +- ”TheMaster" と等しい場合には "Welcome!" と表示します。 +- 別の文字列の場合 -- "Wrong password" を表示します。 +- 空文字または入力がキャンセルされた場合には "Canceled." と表示します。 + + +図: + +![](ifelse_task.svg) + +入れ子の `if` ブロックを使ってください。コードの全体的な読みやすさに気をつけてください。 + +[demo] diff --git a/1-js/02-first-steps/11-logical-operators/article.md b/1-js/02-first-steps/11-logical-operators/article.md index f632e1d8ca..e4841fd332 100644 --- a/1-js/02-first-steps/11-logical-operators/article.md +++ b/1-js/02-first-steps/11-logical-operators/article.md @@ -1,12 +1,10 @@ # 論理演算子 -JavaScriptには3つの論理演算子があります: `||` (OR:論理和), `&&` (AND:論理積), `!` (NOT:否定) +JavaScriptには4つの論理演算子があります: `||` (OR:論理和), `&&` (AND:論理積), `!` (NOT:否定), `??` (Null合体)。ここでは最初の3つを説明し、`??` 演算子は次の記事で説明します。 -それらは "論理" と呼ばれますが、Boolean 型だけでなく、どの型の値にも適用することができます。結果もまた任意の型になります。 +これらは "論理" と呼ばれますが、Boolean 型だけでなく、どの型の値にも適用することができます。結果もまた任意の型になります。 -詳細を見てみましょう: - -[cut] +では、詳細を見ていきましょう。 ## || (OR) @@ -16,9 +14,9 @@ JavaScriptには3つの論理演算子があります: `||` (OR:論理和), `&& result = a || b; ``` -古典的なプログラミングでは、論理和はブール値のみを操作することを意味していました。もしもその引数のいずれかが `true` の場合、それは `true` を返します。そうでなければ `false` を返します。 +古典的なプログラミングでは、論理和は真偽値のみを操作することを意味していました。もしもその引数のいずれかが `true` の場合、それは `true` を返します。そうでなければ `false` を返します。 -JavaScriptでは、演算子は少し難解で強力です。最初にブール値で起こることを見てみましょう。 +JavaScriptでは、演算子は少し難解ですが強力です。最初に真偽値で起こることを見てみましょう。 4つの取りうる論理的な組み合わせがあります: @@ -33,7 +31,7 @@ alert( false || false ); // false もしもオペランドが Boolean でない場合、評価のために Boolean に変換されます。 -例えば、数値 `1` は `true` として扱われ、数値 `0` は -- `false` となります: +例えば、数値 `1` は `true` として扱われ、数値 `0` は `false` となります: ```js run if (1 || 0) { // if( true || false ) のように動作します @@ -41,7 +39,7 @@ if (1 || 0) { // if( true || false ) のように動作します } ``` -ほとんどの場合で、OR `||` は `if` 文の中で、与えられた条件のいずれかが正しいかを確認するために使われます。 +ほとんどの場合、OR `||` は `if` 文の中で、与えられた条件のいずれかが正しいかを確認するのに使われます。 例: @@ -84,7 +82,7 @@ OR `"||"` 演算子は次のように動きます: - それぞれのオペランドで、それを Boolean に変換します。もしも結果が `true` であれば、停止しオペランドの本来の値を返します。 - もしもすべての他のオペランドが評価された場合(i.e. すべて `偽` のとき), 最後のオペランドを返します。 -値は変換されていない本来の形式で返却されます。 +値は変換されていない元の形式で返却されます。 つまり、OR `"||"` のチェーンは最初に真となる値を返し、そのような値がない場合には最後のオペランドが返却されます。 @@ -92,65 +90,51 @@ OR `"||"` 演算子は次のように動きます: ```js run alert( 1 || 0 ); // 1 (1 は真) -alert( true || 'no matter what' ); // (true は真) alert( null || 1 ); // 1 (1 は最初の真値) alert( null || 0 || 1 ); // 1 (最初の真値) + alert( undefined || null || 0 ); // 0 (すべて偽、なので最後の値が返却される) ``` -それは "純粋で古典的な真偽値のみの OR" と比較して、いくつかの興味深い使用方法につながります。 +この結果は、"純粋で昔ながらの真偽値のみの OR" と比較して、いくつかの興味深い使用方法につながります。 1. **変数または式のリストから最初の真値を取得する** - いくつかの変数を持っていると想像してください、それはデータを含むか `null/undefined` になります。そして、私たちは最初のデータを選ぶ必要があります。 + 例えば、`firstName`, `lastName` と `nickName` 変数があり、すべて任意( undefined あるいは偽となる値になりうる)とします。 - そのために OR `||` を使うことができます: + データを持っているものを選び、表示する(あるいは何も設定されていな場合は `"Anonymous"`)のに、OR `||` が利用できます: ```js run - let currentUser = null; - let defaultUser = "John"; + let firstName = ""; + let lastName = ""; + let nickName = "SuperCoder"; *!* - let name = currentUser || defaultUser || "unnamed"; + alert( firstName || lastName || nickName || "Anonymous"); // SuperCoder */!* - - alert( name ); // "John" – 最初の真値です ``` - もしも `currentUser` と `defaultUser` が共に偽の場合、`"unnamed"` が結果になります。 -2. **短絡評価** - - オペランドには値だけでなく、任意の式を使用できます。 ORは左から右へ評価してテストします。 - 真値に到達したとき、評価はストップします。そしてその値が返却されます。この処理は左から右にできるだけ短くなるので、"短絡評価" と呼ばれます。 + すべての変数が偽であれば、`"Anonymous"` が表示されます。 - これは第2引数が与えられた式が副作用を持つ場合にはっきりとわかります。変数の割り当てと同じです: +2. **短絡評価(最小評価)** - もしも私たちは下の例を実行すると、 `x` は割り当てられません。: + OR `||` のもう1つの特徴はいわゆる "短絡" 評価と呼ばれます。 - ```js run no-beautify - let x; - - *!*true*/!* || (x = 1); + つまり、`||` は最初の真値に到達するまで引数を処理し、その後は他の引数には触れることなく、値はすぐに返却されることを意味します。 - alert(x); // undefined, なぜなら (x = 1) は評価されないため - ``` + この機能の重要性は、オペランドが単なる値ではなく、変数の割当や関数呼び出しなどの副作用のある式である場合に明らかになります。 - ...また、もしも1つ目の引数が `false` の場合、 `OR` は次へ行き2つ目の評価を行い、代入をします: + 以下の例を実行した場合、2つ目のメッセージだけが表示されます: ```js run no-beautify - let x; - - *!*false*/!* || (x = 1); - - alert(x); // 1 + *!*true*/!* || alert("not printed"); + *!*false*/!* || alert("printed"); ``` - 代入は単純なケースであり、他の副作用が発生する可能性があります。 - - ご覧のように、このようなユースケースは "`if` をするより短い方法" です。最初のオペランドは真偽値に変換され、もしもそれが偽の場合は2つ目が評価されます。 + 1行目では、OR `||` 演算子が `true` を見るとすぐに評価を停止するため、`alert` は実行されません。 - ときには便利なこともありますが、多くのの場合コードを理解しやすくするために "正規の" `if` を使う方がよいです。 + 条件の左側が false のときにだけコマンドを実行するためにこの特徴を利用する人もいます。 ## && (AND) @@ -197,7 +181,7 @@ if (1 && 0) { // true && false として評価される result = value1 && value2 && value3; ``` -AND `"&&"` 演算子は次のようにします: +AND `"&&"` 演算子は次のように動きます: - 左から右にオペランドを評価します。 - それぞれのオペランドで、それを Boolean に変換します。もしも結果が `false` の場合、ストップしそのオペランドの本来の値を返します。 @@ -221,8 +205,7 @@ alert( null && 5 ); // null alert( 0 && "no matter what" ); // 0 ``` -より多くの値を渡すこともできます。 -どのように最初の偽値が返却されるか見てください。: +より多くの値を渡すこともできます。どのように最初の偽値が返却されるか見てください。: ```js run alert( 1 && 2 && null && 3 ); // null @@ -235,16 +218,13 @@ alert( 1 && 2 && 3 ); // 3, 最後のオペランド ``` ````smart header="AND `&&` は OR `||` の前に実行します" -AND `&&` 演算子の優先順位は OR `||` よりも高いです。そのため、ORの前の実行されます。 +AND `&&` 演算子の優先順位は OR `||` よりも高いです。 -下のコードでは、`1 && 0` が最初に計算されます: - -```js run -alert( 5 || 1 && 0 ); // 5 -``` +そのため、コード `a && b || c && d` は `&&` 式が括弧の中にある場合 `(a && b) || (c && d)` と本質的に同じです。 ```` -ORのように、AND `&&` 演算子は `if` に置き換えることができるときもあります。 +````warn header="`if` を `||` や `&&` に置き換えないでください" +時々、AND `&&` 演算子を "`if`を短く書く方法" として利用する人がいます。 例: @@ -256,19 +236,17 @@ let x = 1; `&&` の右側のアクションは、その評価に到達した場合にのみ実行されます。つまり: `(x > 0)` が true の場合のみです。 -なので、基本的にそのための類似の方法があります: +なので、基本的に同じことをする別の方法があります: ```js run let x = 1; -if (x > 0) { - alert( 'Greater than zero!' ); -} +if (x > 0) alert( 'Greater than zero!' ); ``` -`&&` を含むバリアントは、より短いように見えます。しかし `if` はより明白で、少し読みやすい傾向にあります。 +`&&` を含むやり方は、より短いように見えますが、`if` はより明白で、読みやすい傾向にあります。そのため、すべての構文をその目的に合わせて使うことを推奨します。条件判定が必要なら `if` を、論理積が必要なら `&&` を使います。 +```` -なので、すべての構成要素を目的に応じて使うことを推奨します。もし if が必要なら `if` を使います。AND が必要なら `&&` を使います。 ## ! (NOT) @@ -280,7 +258,7 @@ if (x > 0) { result = !value; ``` -演算子は1つの引数を受けいれ、次のようにします: +演算子は1つの引数を取り、次のようにします: 1. オペランドを真偽値型に変換します: `true/false`。 2. 逆の値を返します。 @@ -292,7 +270,7 @@ alert( !true ); // false alert( !0 ); // true ``` -2つの否定 `!!` は時々値を真偽値型に変換するために使われます: +2つの否定 `!!` は値を真偽値型に変換するために使われることがあります: ```js run alert( !!"non-empty string" ); // true @@ -307,3 +285,5 @@ alert( !!null ); // false alert( Boolean("non-empty string") ); // true alert( Boolean(null) ); // false ``` + +NOT `!` の優先順はすべての論理演算子でもっとも高いので、`&&` や `||` よりも常に最初に実行されます。 diff --git a/1-js/02-first-steps/12-nullish-coalescing-operator/article.md b/1-js/02-first-steps/12-nullish-coalescing-operator/article.md new file mode 100644 index 0000000000..fe1db3bfc9 --- /dev/null +++ b/1-js/02-first-steps/12-nullish-coalescing-operator/article.md @@ -0,0 +1,169 @@ +# NULL合体演算子(Nullish coalescing operator) '??' + +[recent browser="new"] + +NULL合体演算子は2つの疑問符 `??` で記述されます。 + +これは `null` と `undefined` を同じように扱うので、この記事では特別な用語を使用します。`null` でも `undefined` でも無いときは、"定義済み" である、と言います。 + +`a ?? b` の結果は: +- `a` が `null` あるいは `undefined` でなければ `a`, +- それ以外の場合は `b`. + +つまり、`??` は `null/undefined` でなければ最初の引数を返し、それ以外の場合は2つ目を返します。 + +NULL合体演算子はまったく新しいものではありません。2つのうちから、最初の "定義済み" の値を取得するには良い構文です。 + +既に知っている演算子を使用して `result = a ?? b` を書き直すことができます: + +```js +result = (a !== null && a !== undefined) ? a : b; +``` + +これで、`??` がすることがなにか明確ですね。これがどこで役立つが見ていきましょう。 + +`??` の一般的なユースケースは、潜在的に未定義の変数のデフォルト値を提供することです。 + +例えば、ここでは定義済みであれば `user` を、そうでなければ `Anonymous` を表示します: + +```js run +let user; + +alert(user ?? "Anonymous"); // Anonymous (user は未定義) +``` + +こちらは名前が割り当てられた `user` の例です: + +```js run +let user = "John"; + +alert(user ?? "Anonymous"); // John (user は定義済み) +``` + +`??` のシーケンスを使用して、`null/undefined` ではないリストから最初の値を選択することも可能です。 + +変数 `firstName`, `lastName` or `nickName` にユーザデータがあるとしましょう。ユーザが値を入れなかった場合、未定義かもしれません。 + +これらの変数の1つを使用してユーザ名を表示、あるいはすべて未定義の場合には "Anonymous" と表示したいです。 + +そのために `??` 演算子を使用しましょう: + +```js run +let firstName = null; +let lastName = null; +let nickName = "Supercoder"; + +// 最初の null/undefined でない値を表示します +*!* +alert(firstName ?? lastName ?? nickName ?? "Anonymous"); // Supercoder +*/!* +``` + +## || との比較 + +[前のチャプター](info:logical-operators#or-finds-the-first-truthy-value) で説明したように、OR `||` 演算子は `??` と同じ方法で利用することができます。 + +例えば、上のコードで `??` を `||` に置き換えることができ、同じ結果を得ることができます: + +```js run +let firstName = null; +let lastName = null; +let nickName = "Supercoder"; + +// 最初の真値を表示 +*!* +alert(firstName || lastName || nickName || "Anonymous"); // Supercoder +*/!* +``` + +歴史的には、OR `||` 演算子が最初にありました。JavaScript の登場以来存在しているため、開発者は長い間そのような目的で使用していました。 + +一方、NULL合体演算子 `??` が JavaScript に追加されたのは最近のことで、その理由は人々が `||` にあまり満足していなかったためです。 + +重要な違いは次の通りです: +- `||` は最初の *真* の値を返します。 +- `??` は最初の *定義済み* の値を返します。 + +言い換えると、`||` は `false`, `0`, 空文字 `""` と `null/undefined` を区別しません。それらはすべて同じ偽値です。これらのいずれかが `||` の引数の最初にある場合は、2つ目の引数が結果として取得されます。 + +ただし、実際には、変数が `null/undefined` の場合にのみデフォルト値を使用したい場合があります。つまり、値が本当に未知/設定されていない場合です。 + +例として次を考えましょう: + +```js run +let height = 0; + +alert(height || 100); // 100 +alert(height ?? 100); // 0 +``` + +- `height || 100` は `height` が偽値になるかをチェックし、それは `0` であり偽です。 + - なので、`||` の結果は2つ目の引数である `100` です。 +- `height ?? 100` は `height` が `null/undefined` かをチェックしますが、そうではありません。 + - なので、結果は `height` の "まま"、つまり `0` です。 + +実践では、高さゼロはしばしば有効な値であり、デフォルト値で置き換えられるべきではありません。こういった場合、`??` が正しい動作をします。 + +## 優先順位 + +`??` の優先順位は、おおよそ `||` と同じですが、少しだけ低いです。[MDN テーブル](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table) で `5` です(`||` は `6`)。 + +つまり、`||` と同様に NULL合体演算子 `??` は `=` と `?` の前に評価されますが、`+` や `*` などの他のほとんどの演算子の後に評価されます。 + +したがって、他の演算子を含む式で `??` で値を選択したい場合は、括弧を追加することを検討してください: + +```js run +let height = null; +let width = null; + +// 重要: 括弧を使用します +let area = (height ?? 100) * (width ?? 50); + +alert(area); // 5000 +``` + +そうでない場合、括弧を省略すると `*` は `??` よりも優先度が高いため、最初に実行され、正しくない結果になるでしょう。 + +```js +// 括弧なし +let area = height ?? 100 * width ?? 50; + +// ...これと同じように動作します (恐らくしたいことではないはずです): +let area = height ?? (100 * width) ?? 50; +``` + +### && や || と一緒に ?? を使用する + +安全上の理由により、JavaScript は優先順位が括弧で明示的に指定されていない限り、`&&` や `||` 演算子と一緒に `??` を用いることを禁止しています。 + +次のコードは構文エラーになります: + +```js run +let x = 1 && 2 ?? 3; // Syntax error +``` + +この制限には当然議論の余地がありますが、人々が `||` から `??` に切り替え始めるときに、プログラミングのミスを避ける目的で言語仕様に追加されました。 + +回避するには明示的に括弧を使用します: + +```js run +*!* +let x = (1 && 2) ?? 3; // 動作します +*/!* + +alert(x); // 2 +``` + +## サマリ + +- Null合体演算子 `??` は一覧から "定義済み" の値を選択するための簡単な方法を提供します。 + + 変数にデフォルト値を代入するために使用されます: + + ```js + // height が null あるいは undefined であれば height=100 を設定します + height = height ?? 100; + ``` + +- 演算子 `??` は優先度が低く、`?` や `=` よりも少し高い程度です。そのため、式の中で使用する際には括弧を追加することを検討してください。 +- 明示的な括弧なしに `||` や `&&` と一緒に利用することは禁止されています。 diff --git a/1-js/02-first-steps/12-while-for/6-repeat-until-correct/task.md b/1-js/02-first-steps/12-while-for/6-repeat-until-correct/task.md deleted file mode 100644 index bf8a443602..0000000000 --- a/1-js/02-first-steps/12-while-for/6-repeat-until-correct/task.md +++ /dev/null @@ -1,13 +0,0 @@ -importance: 5 - ---- - -# 入力が正しいまで繰り返す - -`100` より大きい数値を入力するプロンプトを書いてください。もし訪問者が別の数値を入力したら -- 再び入力を訪ねます。 - -ループは、訪問者が `100` より大きい値を入力するか、入力をキャンセル/空行の入力をするまで訪ねます。 - -ここでは、訪問者は数値のみを入力すると仮定します。このタスクでは、非数値に対する特別な処理を実装する必要はありません。 - -[demo] diff --git a/1-js/02-first-steps/12-while-for/7-list-primes/task.md b/1-js/02-first-steps/12-while-for/7-list-primes/task.md deleted file mode 100644 index 1583799231..0000000000 --- a/1-js/02-first-steps/12-while-for/7-list-primes/task.md +++ /dev/null @@ -1,17 +0,0 @@ -importance: 3 - ---- - -# 素数の出力 - -`1` よりも大きい整数で、`1` と自身以外では、余りなく割ることができない場合、その数値は [素数(prime)](https://en.wikipedia.org/wiki/Prime_number) と呼ばれます。 - -つまり、`n > 1` は `1` と `n` を除いて均等に分割できない場合、素数です。 - -例えば、`5` は素数です。なぜなら、`2`, `3` と `4` ではあまり無く割ることができなからです。 - -**`2` から `n` の範囲で、素数を出力するコードを書きなさい。** - -`n = 10` の場合、結果は `2,3,5,7` です。 - -P.S. コードは任意の `n` で動作させてください。固定値でハードコードはしないでください。 diff --git a/1-js/02-first-steps/12-while-for/article.md b/1-js/02-first-steps/12-while-for/article.md deleted file mode 100644 index 3197bcb427..0000000000 --- a/1-js/02-first-steps/12-while-for/article.md +++ /dev/null @@ -1,391 +0,0 @@ -# ループ: while と for - -私たちはしばしば連続して何度も似たような処理を実行する必要があります。 - -例えば、1つずつリストから商品を出力する必要がある場合、もしくは、単に1から10の数値それぞれに同じコードを実行する場合。 - -*ループ* は複数回コードの同じ部分を繰り返す方法です。 - -[cut] - -## "while" ループ - -`while` ループは次の構文を持っています: - -```js -while (condition) { - // code - // いわゆる "ループ本体" -} -``` - -`condition` が `true` の間、ループの本体の `code` が実行されます。 - -例えば、下のループは `i < 3` の間、`i` を出力します: - -```js run -let i = 0; -while (i < 3) { // 0, 次に 1, 次に 2 を表示 - alert( i ); - i++; -} -``` - -ループ本体の1つの実行は *イテレーション* と呼ばれます。上の例のループは3つのイテレーションをします。 - -もしも、上の例に `i++` がない場合、ループは (理論上は) 永遠に繰り返されます。実際には、ブラウザはこのようなループを止める方法を提供しており、サーバサイドJavaScriptではそのプロセスを殺すことができます。 - -任意の式または変数は、単に比較ではなくループ条件になります。それらは評価され `while` によって真偽値に変換されます。 - -たとえば、`while (i != 0)` をより短く書く方法として`while (i)`があります: - -```js run -let i = 3; -*!* -while (i) { // i が 0 になったとき、条件が偽になり、ループが止まります -*/!* - alert( i ); - i--; -} -``` - -````smart header="本体が1行の場合、括弧は必須ではありません" -もしもループの本体が1つの文である場合、括弧`{…}`を省略することができます: - -```js run -let i = 3; -*!* -while (i) alert(i--); -*/!* -``` -```` - -## "do..while" ループ - -条件チェックは `do..while` 構文を使うことでループ本体の *下に* 移動することができます。: - -```js -do { - // loop body -} while (condition); -``` - -ループは最初に本体を実行します、次に条件をチェックし、真となる間実行を繰り返します。 - -例: - -```js run -let i = 0; -do { - alert( i ); - i++; -} while (i < 3); -``` - -この構文の形式は、条件が真になるかどうかに関わらず、**少なくとも1度** はループ本体を実行したい場合を除き、ほとんど使われません。通常、他の形式が好まれます: `while(…) {…}` - -## "for" ループ - -`for` ループは最も使われるものの1つです。 - -このようになります: - -```js -for (begin; condition; step) { - // ... loop body ... -} -``` - -例でこれらのパーツの意味を学びましょう。下のループは `0` から `3` まで(`3` は含みません)、`i` に対して `alert(i)` を実行します。: - -```js run -for (let i = 0; i < 3; i++) { // 0, 次に 1, 次に 2 を表示 - alert(i); -} -``` - -`for` 文を部分的に調べてみましょう: - -| パート | | | -|-------|----------|----------------------------------------------------------------------------| -| begin | `i = 0` | ループに入ると1度実行されます。 | -| condition | `i < 3`| すべてのループのイテレーションの前にチェックされます、もしも false の場合ループを停止します。 | -| step| `i++` | 各イテレーションで、条件チェックの前に本体の後に実行されます。 | -| body | `alert(i)`| 条件が真の間繰り返し実行されます。 | - -一般的なループアルゴリズムは次のように動作します: -``` -begin を実行 -→ (if condition → body を実行し step を実行) -→ (if condition → body を実行し step を実行) -→ (if condition → body を実行し step を実行) -→ ... -``` - -もし初めてループを使う場合、この例に戻って、紙の上でステップ毎にどのように動作するかを再現することが恐らく役立つでしょう。 - -これが今のケースで正確に起こっていることです: - -```js -// for (let i = 0; i < 3; i++) alert(i) - -// begin を実行 -let i = 0 -// if condition → body を実行し step を実行 -if (i < 3) { alert(i); i++ } -// if condition → body を実行し step を実行 -if (i < 3) { alert(i); i++ } -// if condition → body を実行し step を実行 -if (i < 3) { alert(i); i++ } -// ...終わり, 今 i == 3 なので -``` - -````smart header="インライン変数宣言" -ここで "カウンタ" 変数 `i` はループの中で正しく宣言されます。それは "インライン" 変数宣言と呼ばれます。 -このような変数はループの中でだけ見えます。 - -```js run -for (*!*let*/!* i = 0; i < 3; i++) { - alert(i); // 0, 1, 2 -} -alert(i); // エラー, そのような変数はありません -``` - -変数を宣言する代わりに、存在するもの使うことも出来ます: - -```js run -let i = 0; - -for (i = 0; i < 3; i++) { // 既存の変数を使用 - alert(i); // 0, 1, 2 -} - -alert(i); // 3, ループの外で宣言されているので見える -``` - -```` - - -### 一部分の省略 - -`for` の任意の一部分を省略することができます。 -例えば、ループの最初で何もする必要がなければ、`begin` を省略することができます。 - -このように: - -```js run -let i = 0; // すでに i を宣言し代入済み - -for (; i < 3; i++) { // "begin" 不要 - alert( i ); // 0, 1, 2 -} -``` - -同じように `step` パートも除去することができます。: - -```js run -let i = 0; - -for (; i < 3;) { - alert( i++ ); -} -``` - -ループは `while (i < 3)` と同じになりました。 - -実際にはすべてを除くこともできます、それは無限ループになります: - -```js -for (;;) { - // 制限なしで繰り返し -} -``` - -2つの `for` のセミコロン `;` は必須であることに注意してください。ない場合は構文エラーになります。 - -## ループの終わり - -通常、ループは条件が偽になると終了します。 - -しかし、いつでもループから出ることを強制することができます。そのための特別な `break` ディレクティブがあります。 - -例えば、以下のループはユーザに一連の数字を入力するよう求めますが、文字以外が入力されたとき "中断" します。: - -```js -let sum = 0; - -while (true) { - - let value = +prompt("Enter a number", ''); - -*!* - if (!value) break; // (*) -*/!* - - sum += value; - -} -alert( 'Sum: ' + sum ); -``` - -もしもユーザが空を入力、もしくは入力をキャンセルした場合、`break` ディレクティブは行 `(*)` で有効になります。 -それはループをすぐに停止し、ループ後の最初の行へ制御を渡します。つまり、`alert` です。 - -"必要に応じた無限ループ + `break`" の組み合わせは、ループの最初/最後ではなくその間、もしくは本体の様々な場所で条件をチェックする必要がある状況で最適です。 - -## 次のイテレーションに進む - -`continue` ディレクティブは `break` の "軽量版" です。ループ全体はストップしません。その代わりに、現在のイテレーションを停止し、新しいイテレーションのスタートを強制します(もしも条件が真であれば)。 - -私たちは、現在のイテレーションが完了し次へ移動したいときに使います。 - -以下のループは奇数値だけを出力するために `continue` を使用しています: - -```js run no-beautify -for (let i = 0; i < 10; i++) { - - // true の場合、本体の残りのパートをスキップ - *!*if (i % 2 == 0) continue;*/!* - - alert(i); // 1, 次に 3, 5, 7, 9 -} -``` - -`i` の値が偶数の場合、`continue` ディレクティブは本体の実行を停止し、`for` の次のイテレーションへ制御を渡しします(次の番号で)。 -したがって、`alert` は奇数値に対してのみ実行されます。 - -````smart header="ディレクティブ `continue` を使うと入れ子のレベルを減らせます" -奇数値を表示するループはこのように書くこともできます: - -```js -for (let i = 0; i < 10; i++) { - - if (i % 2) { - alert( i ); - } - -} -``` - -技術的な観点からは、これは上の例と同じです。確かに、`continue` の代わりに `if` ブロックでコードをラップするだけです。 - -しかし、副作用として括弧のネストが1段深くなります。もしも `if` のコードの中文が長い場合、全体の可読性が下がる可能性があります。 -```` - -````warn header="`?` の右側には `break/continue` はありません" -式ではない構文構造は `'?'` の中では使うことはできないことに注意してください。特に、ディレクティブ `break/continue` はそこでは許可されません。 - -例えば、このコードを使うとします: - -```js -if (i > 5) { - alert(i); -} else { - continue; -} -``` - -...そして、疑問符を使って書き直します: - -```js no-beautify -(i > 5) ? alert(i) : *!*continue*/!*; // continue はここでは使えません -``` - -...それは動作を停止します。このようなコードは構文エラーになります: - - -これは `if` の代わりに疑問符演算子 `'?'` を使用しない別の理由です。 -```` - -## break/continue のためのラベル - -時々、複数のネストしたループから一度で抜け出す必要があります。 - -例えば、下のコードでは 座標 `(i, j)` を `(0,0)` から `(3,3)` へプロンプトするよう `i` と `j` をループします: - -```js run no-beautify -for (let i = 0; i < 3; i++) { - - for (let j = 0; j < 3; j++) { - - let input = prompt(`Value at coords (${i},${j})`, ''); - - // 仮にここで終了して下にある Done をしたい場合にはどうすればよいでしょう? - - } -} - -alert('Done!'); -``` - -ユーザが入力をキャンセルした場合、処理をストップする方法が必要です。 - -`input` の後の通常の `break` は内部ループのみの終了です。それだけでは十分ではありません。 -ここでラベルが助けにきます。 - -*ラベル* はループの前のコロンがついた識別子です: -```js -labelName: for (...) { - ... -} -``` - -ループの中の `break ` 文はラベルまで終了にします。 - -次のようになります: - -```js run no-beautify -*!*outer:*/!* for (let i = 0; i < 3; i++) { - - for (let j = 0; j < 3; j++) { - - let input = prompt(`Value at coords (${i},${j})`, ''); - - // 文字から文字またはキャンセルされた場合、両方のループから抜ける - if (!input) *!*break outer*/!*; // (*) - - // 値に何かをする処理... - } -} -alert('Done!'); -``` - -上のコードで、`break outer` は `outer` と名付けされたラベルを上に探し、そのループを抜けます。 - -そのため、制御は `(*)` から `alert('Done!')` にまっすぐに進みます。 - -ラベルを別の行に移動させることもできます: - -```js no-beautify -outer: -for (let i = 0; i < 3; i++) { ... } -``` - -`continue` ディレクティブもまたラベルと一緒に使うことが出来ます。このケースでは、実行はラベル付けされたループの次のイテレーションにジャンプします。 - -````warn header="ラベルは \"goto\" ではありません" -ラベルはコードの任意の場所にジャンプすることはできません。 - -例えば、このようにすることは出来ません: -```js -break label; // label にジャンプ? いいえ。 - -label: for (...) -``` - -`break/continue` への呼び出しはループの中からだけ可能です、またラベルはそのディレクティブから上のどこかにある必要があります。 -```` - -## サマリ - -3つのループのタイプについて説明しました: - -- `while` -- 条件は各イテレーションの前にチェックされます。 -- `do..while` -- 条件は各イテレーションの後にチェックされます。 -- `for (;;)` -- 条件は各イテレーションの前にチェックされ、追加の設定が利用可能です。 - -"無限" ループを作るために、通常は `while(true)` 構造が使われます。このようなループは他の他のループと同様に `break` ディレクティブで停止することができます。 - -もしも現在のイテレーションで何もしたくなく、次のイテレーションに進みたい場合は、`continue` ディレクティブを使います。 - -`break/continue` はループの前のラベルをサポートします。ラベルはネストを抜け、外のループに行くための `break/continue` のための方法です。 diff --git a/1-js/02-first-steps/13-switch/article.md b/1-js/02-first-steps/13-switch/article.md deleted file mode 100644 index 651c97f83e..0000000000 --- a/1-js/02-first-steps/13-switch/article.md +++ /dev/null @@ -1,176 +0,0 @@ -# switch文 - -`switch` 文は複数の `if` チェックに置換できます。 - -それは値を複数のバリアントと比較するための、よりわかりやすい方法を提供します。 - -[cut] - -## 構文 - -`switch` は1つ以上の `case` ブロックを持ち、 オプションで default を持ちます。 - -このようになります: - -```js no-beautify -switch(x) { - case 'value1': // if (x === 'value1') - ... - [break] - - case 'value2': // if (x === 'value2') - ... - [break] - - default: - ... - [break] -} -``` - -- `x` の値は、最初の `case` (それは `value1`)の値と厳密な等価のチェックをされます、そして2つ目(`value2`)と続きます。 -- もしも等価が見つかった場合、 `switch` は該当する `case` から始まるコードを実行し始めます。最も近い `break` まで(もしくは `switch` の終わりまで)。 -- もしもマッチするケースが無い場合は、`default` コードが実行されます(もしも存在すれば) - -## 例 - -`switch` の例です(実行されるコードはハイライトされています) - -```js run -let a = 2 + 2; - -switch (a) { - case 3: - alert( 'Too small' ); - break; -*!* - case 4: - alert( 'Exactly!' ); - break; -*/!* - case 5: - alert( 'Too large' ); - break; - default: - alert( "I don't know such values" ); -} -``` - -ここで、 `switch` は `3` というバリアントの最初の `case` から `a` と比較を始めます。マッチはしません。 - -そして `4` です。マッチするので、`case 4` から最も近い `break` までの実行を開始します。 - -**もしも `break` がない場合、チェックなしで次の `case` の実行を継続します。** - - -`break` なしの例です: - -```js run -let a = 2 + 2; - -switch (a) { - case 3: - alert( 'Too small' ); -*!* - case 4: - alert( 'Exactly!' ); - case 5: - alert( 'Too big' ); - default: - alert( "I don't know such values" ); -*/!* -} -``` - -上の例では、3つの `alert` の順序実行になるでしょう。 - -```js -alert( 'Exactly!' ); -alert( 'Too big' ); -alert( "I don't know such values" ); -``` - -````smart header="どのような式も `switch / case` 引数になります" -`switch` と `case` の両方は任意の表現が可能です。 - -例: - -```js run -let a = "1"; -let b = 0; - -switch (+a) { -*!* - case b + 1: - alert("this runs, because +a is 1, exactly equals b+1"); - break; -*/!* - - default: - alert("this doesn't run"); -} -``` -ここで `+a` は `1` が与えられ、 `case` で `b + 1` と比較されます。そして、対応するコードが実行されます。 -```` - -## "case"のグルーピング - -同じコードを共有するいくつかの `case` のバリアントはグループ化できます。 - -たとえば、`case 3` と `case 5` で同じコードを実行したい場合: - -```js run no-beautify -let a = 2 + 2; - -switch (a) { - case 4: - alert('Right!'); - break; - -*!* - case 3: // (*) 2つのケースをグループ化 - case 5: - alert('Wrong!'); - alert("Why don't you take a math class?"); - break; -*/!* - - default: - alert('The result is strange. Really.'); -} -``` - -今、`3` と `5` は同じメッセージを表示します。 - -ケースを "グループ化" する機能は、`break` がない場合の `switch/case` の動作の副作用です。ここで `case 3` の実行は、`break` がないので `(*)` の行から始まり、`case 5` を通り抜けます。 - -## タイプの問題 - -等価チェックは常に厳密であることに注目しましょう。 -マッチするために値は同じタイプである必要があります。 - -たとえば、このコードを考えてみましょう: - -```js run -let arg = prompt("Enter a value?") -switch (arg) { - case '0': - case '1': - alert( 'One or zero' ); - break; - - case '2': - alert( 'Two' ); - break; - - case 3: - alert( 'Never executes!' ); - break; - default: - alert( 'An unknown value' ) -} -``` - -1. `0`, `1` の場合、最初の `alert` が実行されます。 -2. `2` の場合は2つ目の `alert` が実行されます。 -3. しかし `3` の場合、`prompt` の結果は文字列の `"3"`なので、数字の `3` との厳密な等価 `===` ではありません。そのため、`case 3` はデッドコードです! `default` バリアントが実行されるでしょう。 diff --git a/1-js/02-first-steps/12-while-for/1-loop-last-value/solution.md b/1-js/02-first-steps/13-while-for/1-loop-last-value/solution.md similarity index 100% rename from 1-js/02-first-steps/12-while-for/1-loop-last-value/solution.md rename to 1-js/02-first-steps/13-while-for/1-loop-last-value/solution.md diff --git a/1-js/02-first-steps/12-while-for/1-loop-last-value/task.md b/1-js/02-first-steps/13-while-for/1-loop-last-value/task.md similarity index 100% rename from 1-js/02-first-steps/12-while-for/1-loop-last-value/task.md rename to 1-js/02-first-steps/13-while-for/1-loop-last-value/task.md diff --git a/1-js/02-first-steps/12-while-for/2-which-value-while/solution.md b/1-js/02-first-steps/13-while-for/2-which-value-while/solution.md similarity index 100% rename from 1-js/02-first-steps/12-while-for/2-which-value-while/solution.md rename to 1-js/02-first-steps/13-while-for/2-which-value-while/solution.md diff --git a/1-js/02-first-steps/12-while-for/2-which-value-while/task.md b/1-js/02-first-steps/13-while-for/2-which-value-while/task.md similarity index 100% rename from 1-js/02-first-steps/12-while-for/2-which-value-while/task.md rename to 1-js/02-first-steps/13-while-for/2-which-value-while/task.md diff --git a/1-js/02-first-steps/12-while-for/3-which-value-for/solution.md b/1-js/02-first-steps/13-while-for/3-which-value-for/solution.md similarity index 100% rename from 1-js/02-first-steps/12-while-for/3-which-value-for/solution.md rename to 1-js/02-first-steps/13-while-for/3-which-value-for/solution.md diff --git a/1-js/02-first-steps/12-while-for/3-which-value-for/task.md b/1-js/02-first-steps/13-while-for/3-which-value-for/task.md similarity index 100% rename from 1-js/02-first-steps/12-while-for/3-which-value-for/task.md rename to 1-js/02-first-steps/13-while-for/3-which-value-for/task.md diff --git a/1-js/02-first-steps/12-while-for/4-for-even/solution.md b/1-js/02-first-steps/13-while-for/4-for-even/solution.md similarity index 100% rename from 1-js/02-first-steps/12-while-for/4-for-even/solution.md rename to 1-js/02-first-steps/13-while-for/4-for-even/solution.md diff --git a/1-js/02-first-steps/12-while-for/4-for-even/task.md b/1-js/02-first-steps/13-while-for/4-for-even/task.md similarity index 100% rename from 1-js/02-first-steps/12-while-for/4-for-even/task.md rename to 1-js/02-first-steps/13-while-for/4-for-even/task.md diff --git a/1-js/02-first-steps/12-while-for/5-replace-for-while/solution.md b/1-js/02-first-steps/13-while-for/5-replace-for-while/solution.md similarity index 100% rename from 1-js/02-first-steps/12-while-for/5-replace-for-while/solution.md rename to 1-js/02-first-steps/13-while-for/5-replace-for-while/solution.md diff --git a/1-js/02-first-steps/12-while-for/5-replace-for-while/task.md b/1-js/02-first-steps/13-while-for/5-replace-for-while/task.md similarity index 100% rename from 1-js/02-first-steps/12-while-for/5-replace-for-while/task.md rename to 1-js/02-first-steps/13-while-for/5-replace-for-while/task.md diff --git a/1-js/02-first-steps/12-while-for/6-repeat-until-correct/solution.md b/1-js/02-first-steps/13-while-for/6-repeat-until-correct/solution.md similarity index 100% rename from 1-js/02-first-steps/12-while-for/6-repeat-until-correct/solution.md rename to 1-js/02-first-steps/13-while-for/6-repeat-until-correct/solution.md diff --git a/1-js/02-first-steps/13-while-for/6-repeat-until-correct/task.md b/1-js/02-first-steps/13-while-for/6-repeat-until-correct/task.md new file mode 100644 index 0000000000..94e8841649 --- /dev/null +++ b/1-js/02-first-steps/13-while-for/6-repeat-until-correct/task.md @@ -0,0 +1,13 @@ +importance: 5 + +--- + +# 正しい値が入力されるまで繰り返す + +`100` より大きい数値を入力するプロンプトを書いてください。もし訪問者が別の数値を入力したら -- 再度、入力を促します。 + +ループは、訪問者が `100` より大きい値を入力するか、入力をキャンセル/空行の入力をするまで訪ねます。 + +ここでは、訪問者は数値のみを入力すると仮定します。このタスクでは、非数値に対する特別な処理を実装する必要はありません。 + +[demo] diff --git a/1-js/02-first-steps/12-while-for/7-list-primes/solution.md b/1-js/02-first-steps/13-while-for/7-list-primes/solution.md similarity index 100% rename from 1-js/02-first-steps/12-while-for/7-list-primes/solution.md rename to 1-js/02-first-steps/13-while-for/7-list-primes/solution.md diff --git a/1-js/02-first-steps/13-while-for/7-list-primes/task.md b/1-js/02-first-steps/13-while-for/7-list-primes/task.md new file mode 100644 index 0000000000..cccb621837 --- /dev/null +++ b/1-js/02-first-steps/13-while-for/7-list-primes/task.md @@ -0,0 +1,17 @@ +importance: 3 + +--- + +# 素数の出力 + +`1` よりも大きい整数で、`1` と自身以外では、余りなく割ることができない場合、その数値は [素数(prime)](https://ja.wikipedia.org/wiki/%E7%B4%A0%E6%95%B0) と呼ばれます。 + +つまり、1より大きいnが `1` と `n` 以外では割り切れない場合、素数となります。 + +例えば、`5` は素数です。なぜなら、`2`, `3` と `4` ではあまり無く割ることができなからです。 + +**`2` から `n` の範囲で、素数を出力するコードを書きなさい。** + +`n = 10` の場合、結果は `2,3,5,7` です。 + +P.S. コードは任意の `n` で動作させてください。固定値でハードコードはしないでください。 diff --git a/1-js/02-first-steps/13-while-for/article.md b/1-js/02-first-steps/13-while-for/article.md new file mode 100644 index 0000000000..292f02a55d --- /dev/null +++ b/1-js/02-first-steps/13-while-for/article.md @@ -0,0 +1,410 @@ +# ループ: while と for + +繰り返し処理は頻繁に必要になります。 + +例えば、1つずつリストから商品を出力するような場合や、単に1から10の数値それぞれに同じコードを実行する場合などです。 + +*ループ* は複数回同じコードを繰り返す手段です。 + +```smart header="for..of と for..in ループ" +上級者向けのちょっとしたお知らせです。 + +この記事は基本的なループ: `while`, `do..while` と `for(..;..;..)` のみの説明をしています。 + +他の種類のループを検索してこの記事にたどり着いたのであれば、以下を参照してください。 + +- オブジェクトプロパティのループは [for..in](info:object#forin) です。 +- 配列や反復可能オブジェクトに対するループは [for..of](info:array#loops) と [iterables](info:iterable) です。 + +それ以外の場合は、続けて読んでみてください。 +``` + +## "while" ループ + +`while` ループは次の構文になります: + +```js +while (condition) { + // code + // いわゆる "ループ本体" です +} +``` + +`condition` が `true` の間、ループの本体の `code` が実行されます。 + +例えば、以下のループは `i < 3` の間、`i` を出力します: + +```js run +let i = 0; +while (i < 3) { // 0, 次に 1, 次に 2 を表示 + alert( i ); + i++; +} +``` + +ループ本体の1回の実行は *イテレーション* と呼ばれます。上の例のループは3回イテレーションします。 + +もしも上の例に `i++` がない場合、ループは (理論上は) 永遠に繰り返されます。実際には、ブラウザはこのようなループを止める方法を提供しており、サーバサイドJavaScriptではそのプロセスを殺すことができます。 + +比較に限らず、どんな式や変数もループの条件にすることができます。条件は `while` によって評価され、真偽値に変換されます。 + +たとえば、`while (i != 0)` をより短く書く方法として`while (i)`があります: + +```js run +let i = 3; +*!* +while (i) { // i が 0 になったとき、条件が偽になり、ループが止まります +*/!* + alert( i ); + i--; +} +``` + +````smart header="本体が1行の場合、括弧は必須ではありません" +ループの本体が単一の文である場合、括弧`{…}`を省略することができます: + +```js run +let i = 3; +*!* +while (i) alert(i--); +*/!* +``` +```` + +## "do..while" ループ + +`do..while` 構文を使うことで、条件チェックをループ本体の *下に* 移動させることができます。: + +```js +do { + // loop body +} while (condition); +``` + +ループは最初に本体を実行した後、条件をチェックし、条件が真である間、本体の実行を繰り返します。 + +例: + +```js run +let i = 0; +do { + alert( i ); + i++; +} while (i < 3); +``` + +この構文の形式は、条件が真になるかどうかに関わらず、**少なくとも1度** はループ本体を実行したい場合にのみ使用されるべきです。通常は他の形式が好まれます: `while(…) {…}` + +## "for" ループ + +`for` ループは最も使われるものの1つです。 + +このようになります: + +```js +for (begin; condition; step) { + // ... loop body ... +} +``` + +例でこれらのパーツの意味を学びましょう。下のループは `i` が `0` から `3` になるまで(`3` は含みません)、 `alert(i)` を実行します。: + +```js run +for (let i = 0; i < 3; i++) { // 0, 次に 1, 次に 2 を表示 + alert(i); +} +``` + +`for` 文を部分的に調べてみましょう: + +| パート | | | +|-------|----------|----------------------------------------------------------------------------| +| begin | `i = 0` | ループに入ると1度実行されます。 | +| condition | `i < 3`| すべてのループのイテレーションの前にチェックされます。条件が偽の場合、ループを停止します。 | +| step| `i++` | 各イテレーションで、条件チェックの前に本体の後に実行されます。 | +| body | `alert(i)`| 条件が真の間繰り返し実行されます。 | + +一般的なループアルゴリズムは次のように動作します: + +``` +begin を実行 +→ (if condition → body を実行し step を実行) +→ (if condition → body を実行し step を実行) +→ (if condition → body を実行し step を実行) +→ ... +``` + +つまり、`begin` を1度実行し、その後、各 `condition` の評価の後、`body` と `step` が実行されるという繰り返しになります。 + +もしループに慣れていない場合は、上の例に戻って、紙の上でステップ毎にどのように動作するかを再現してみると理解しやすいでしょう。 + +これが今のケースで正確に起こっていることです: + +```js +// for (let i = 0; i < 3; i++) alert(i) + +// begin を実行 +let i = 0 +// if condition → body を実行し step を実行 +if (i < 3) { alert(i); i++ } +// if condition → body を実行し step を実行 +if (i < 3) { alert(i); i++ } +// if condition → body を実行し step を実行 +if (i < 3) { alert(i); i++ } +// ...終わり, 今 i == 3 なので +``` + +````smart header="インライン変数宣言" +ここで "カウンタ" 変数 `i` はループの中で正しく宣言されます。それは "インライン" 変数宣言と呼ばれます。このような変数はループの中でだけ見えます。 + +```js run +for (*!*let*/!* i = 0; i < 3; i++) { + alert(i); // 0, 1, 2 +} +alert(i); // エラー, そのような変数はありません +``` + +変数を宣言する代わりに、既存のものを使うこともできます: + +```js run +let i = 0; + +for (i = 0; i < 3; i++) { // 既存の変数を使用 + alert(i); // 0, 1, 2 +} + +alert(i); // 3, ループの外で宣言されているので見える +``` +```` + +### 一部分の省略 + +`for` の一部分は省略することができます。 + +例えば、ループの最初で何もする必要がなければ、`begin` を省略できます。 + +このようになります: + +```js run +let i = 0; // すでに i を宣言し代入済み + +for (; i < 3; i++) { // "begin" 不要 + alert( i ); // 0, 1, 2 +} +``` + +同じように `step` パートも除去することができます。: + +```js run +let i = 0; + +for (; i < 3;) { + alert( i++ ); +} +``` + +ループは `while (i < 3)` と同じになりました。 + +実際にはすべてを除くこともできます。それは無限ループになります: + +```js +for (;;) { + // 制限なしで繰り返し +} +``` + +`for` の2つのセミコロン `;` は必須であることに注意してください。ない場合は構文エラーになります。 + +## ループの終わり + +通常、ループは条件が偽になると終了します。 + +ですが、`break` ディレクティブを利用して、いつでも強制的にループを終わらせることができます。 + +例えば、以下のループはユーザに一連の数字を入力するよう求めますが、数字が入力されなかった場合は "中断" します。: + +```js +let sum = 0; + +while (true) { + + let value = +prompt("Enter a number", ''); + +*!* + if (!value) break; // (*) +*/!* + + sum += value; + +} +alert( 'Sum: ' + sum ); +``` + +もしもユーザが空を入力、もしくは入力をキャンセルした場合、`break` ディレクティブは行 `(*)` で有効になります。それはループをすぐに停止し、ループ後の最初の行へ制御を渡します。つまり、`alert` です。 + +"無限ループ + 必要に応じた `break`" の組み合わせは、ループの最初や最後ではなく、途中や本体の様々な場所で条件をチェックする必要がある状況で最適です。 + +## 次のイテレーションに進む + +`continue` ディレクティブは `break` の "軽量版" です。ループ全体はストップしません。その代わりに、現在のイテレーションを停止し、新しいイテレーションのスタートを強制します(条件が真であれば)。 + +現在のイテレーションが完了し、次へ移動したいときに使います。 + +以下のループは、奇数値のみを出力するよう `continue` を使用しています: + +```js run no-beautify +for (let i = 0; i < 10; i++) { + + // true の場合、本体の残りのパートをスキップ + *!*if (i % 2 == 0) continue;*/!* + + alert(i); // 1, 次に 3, 5, 7, 9 +} +``` + +`i` の値が偶数の場合、`continue` ディレクティブは本体の実行を停止し、`for` の次のイテレーションへ制御を渡します(次の番号で)。したがって、`alert` は奇数値に対してのみ実行されます。 + +````smart header="ディレクティブ `continue` を使うと入れ子のレベルを減らせます" +奇数値を表示するループはこのように書くこともできます: + +```js +for (let i = 0; i < 10; i++) { + + if (i % 2) { + alert( i ); + } + +} +``` + +技術的な観点からは、これは上の例と同じです。確かに、`continue` の代わりに `if` ブロックでコードをラップするだけです。 + +しかし、副作用として括弧のネストが1段深くなります。`if` の中のコードが長い場合、全体の可読性が下がる可能性があります。 +```` + +````warn header="`?` の右側には `break/continue` を入れないでください" +式ではない構文構造は、 三項演算子 `?` の中では使えないことに注意してください。特に、ディレクティブ `break/continue` はそこでは許可されません。 + +例えば、次のようなコードがあるとします: + +```js +if (i > 5) { + alert(i); +} else { + continue; +} +``` + +...これを、疑問符を使って書き直します: + +```js no-beautify +(i > 5) ? alert(i) : *!*continue*/!*; // continue はここでは使えません +``` + +...この場合は動作を停止します。コードは構文エラーになります: + +これは `if` の代わりに疑問符演算子 `?` を使用しない別の理由です。 +```` + +## break/continue のためのラベル + +一度に複数のネストしたループから抜け出すことが必要となる場合があります。 + +例えば、下のコードでは 座標 `(i, j)` を `(0,0)` から `(2,2)` へプロンプトするよう、`i` と `j` をループします: + +```js run no-beautify +for (let i = 0; i < 3; i++) { + + for (let j = 0; j < 3; j++) { + + let input = prompt(`Value at coords (${i},${j})`, ''); + + // もしここで終了して下にある Done をしたい場合にはどうすればよいでしょう? + } +} + +alert('Done!'); +``` + +ユーザが入力をキャンセルした場合、処理をストップする方法が必要です。 + +`input` の後の通常の `break` は内部ループのみの終了です。それだけでは十分ではありません。ここでラベルが救いの手を差し伸べてくれます。 + +*ラベル* は、ループの前のコロンがついた識別子です: + +```js +labelName: for (...) { + ... +} +``` + +ループの中の `break ` 文はラベルまで抜け出します: + +```js run no-beautify +*!*outer:*/!* for (let i = 0; i < 3; i++) { + + for (let j = 0; j < 3; j++) { + + let input = prompt(`Value at coords (${i},${j})`, ''); + + // 文字から文字またはキャンセルされた場合、両方のループから抜ける + if (!input) *!*break outer*/!*; // (*) + + // 値に何かをする処理... + } +} + +alert('Done!'); +``` + +上のコードで、`break outer` は `outer` と名付けされたラベルを上に探し、そのループを抜けます。 + +そのため、制御は `(*)` から `alert('Done!')` にまっすぐに進みます。 + +ラベルを別の行に移動させることもできます: + +```js no-beautify +outer: +for (let i = 0; i < 3; i++) { ... } +``` + +`continue` ディレクティブもラベルと一緒に使うことができます。このケースでは、実行はラベル付けされたループの次のイテレーションにジャンプします。 + +````warn header="ラベルはどこにでも \"ジャンプ\" を許可するものではありません" +ラベルはコードの任意の場所にジャンプすることはできません。 + +例えば、このようにすることは出来ません: + +```js +break label; // 以下のラベルにジャンプはしません + +label: for (...) +``` + +`break` ディレクティブはコードブロックの中にある必要があります。技術的には任意のラベル付けされたコードブロックであれば機能します。 +```js +label: { + // ... + break label; // works + // ... +} +``` + +...ですが、`break` が利用される 99.9% は上の例で見てきたように、ループの内側です。 + +`continue` はループの内側でのみ利用可能です。 +```` + +## サマリ + +3種類のループについて説明しました: + +- `while` -- 条件は各イテレーションの前にチェックされます。 +- `do..while` -- 条件は各イテレーションの後にチェックされます。 +- `for (;;)` -- 条件は各イテレーションの前にチェックされ、追加の設定ができます。 + +"無限" ループを作るには、通常は `while(true)` 構造が使われます。このようなループは他の他のループと同様に `break` ディレクティブで停止することができます。 + +もしも現在のイテレーションで何もしたくなく、次のイテレーションに進みたい場合は、`continue` ディレクティブを使います。 + +`break/continue` はループの前のラベルをサポートします。ラベルは、 `break/continue` でネストされたループを抜けて外側のループに行くための唯一の方法です。 diff --git a/1-js/02-first-steps/14-function-basics/4-pow/task.md b/1-js/02-first-steps/14-function-basics/4-pow/task.md deleted file mode 100644 index e23010de11..0000000000 --- a/1-js/02-first-steps/14-function-basics/4-pow/task.md +++ /dev/null @@ -1,22 +0,0 @@ -importance: 4 - ---- - -# 関数 pow(x,n) - - -Write a function `pow(x,n)` that returns `x` in power `n`. Or, in other words, multiplies `x` by itself `n` times and returns the result. - -`x` の累乗 `n` を返す関数 `pow(x,n)` を書いてください。 つまり、 `x` をそれ自身で `n` 回掛け、その結果を返します。 - -```js -pow(3, 2) = 3 * 3 = 9 -pow(3, 3) = 3 * 3 * 3 = 27 -pow(1, 100) = 1 * 1 * ...*1 = 1 -``` - -`x` と `n` を聞くプロンプトををもつ web ページを作り、`pow(x,n)` の結果を表示しなさい。 - -[demo] - -P.S. このタスクでは、`n` に自然数のみをサポートします。 diff --git a/1-js/02-first-steps/14-function-basics/article.md b/1-js/02-first-steps/14-function-basics/article.md deleted file mode 100644 index dbf8307dab..0000000000 --- a/1-js/02-first-steps/14-function-basics/article.md +++ /dev/null @@ -1,458 +0,0 @@ -# 関数 - -多くの場合、スクリプトの様々な場所で同様のアクションを実行する必要があります。 - -例えば、訪問者がログイン、ログアウトしたり、また他の場所にいる時に見栄の良いメッセージを表示する必要があります。 - -関数はプログラムのメインの "構成要素" です。これによりコードを繰り返すことなく何度も呼び出すことが出来ます。 - -[cut] - -私たちは既に組み込み関数の例を見ています。 `alert(message)`, `prompt(message, default)` や `confirm(question)`です。 -しかし、同じように私達自身も関数を作ることができます。 - -## 関数定義 - -関数を作るために、*関数定義* を使います。 - -これは次のようになります: - -```js -function showMessage() { - alert( 'Hello everyone!' ); -} -``` - -`function` キーワードが最初にきて、次に *関数名* がきます、そして括弧の中に *パラメータ* のリストがきて(上の例では空白です)、最後に関数のコード、"関数本体" です。 - -![](function_basics.png) - -新しい関数はその名前で呼ぶことが出来ます: `showMessage()` - -例: - -```js run -function showMessage() { - alert( 'Hello everyone!' ); -} - -*!* -showMessage(); -showMessage(); -*/!* -``` - -`showMessage()` の呼び出しは関数のコードを実行します。この例では、私たちは2度メッセージを見ます。 - -この例は関数のメインの目的の1つを明確に説明しています: コードの複製を回避します。 - -もしも、メッセージもしくは表示する方法を変更する必要がある場合、1つの場所のコードを修正するだけで十分です。: それを出力する関数です。 - -## ローカル変数 - -関数内の変数定義は関数内でのみ見えます。 - -例: - -```js run -function showMessage() { -*!* - let message = "Hello, I'm JavaScript!"; // ローカル変数 -*/!* - - alert( message ); -} - -showMessage(); // Hello, I'm JavaScript! - -alert( message ); // <-- エラー! 変数は関数のローカルです -``` - -## 外部変数 - -同様に、関数は外部変数にアクセスすることができます, 次の例にように: - -```js run no-beautify -let *!*userName*/!* = 'John'; - -function showMessage() { - let message = 'Hello, ' + *!*userName*/!*; - alert(message); -} - -showMessage(); // Hello, John -``` - -関数は外部変数に対してフルアクセス権を持ちます。同様にそれを変更することができます。 - -例: - -```js run -let *!*userName*/!* = 'John'; - -function showMessage() { - *!*userName*/!* = "Bob"; // (1) 外部変数の変更 - - let message = 'Hello, ' + *!*userName*/!*; - alert(message); -} - -alert( userName ); // 関数呼び出しの前は *!*John*/!* - -showMessage(); - -alert( userName ); // *!*Bob*/!*, 関数によって値が変更されました -``` - -外部変数は、ローカル変数が存在しない場合にのみ使われます。そのため、私たちが `let` を忘れていたら偶発的な修正が必要になるかもしれません。 - -もしも同じ名前の変数が関数内に宣言されていると、それは外部変数を *隠します*。例えば、以下のコードでは関数はローカルの `userName` を使います。外部の `userName` は無視されます。 - -```js run -let userName = 'John'; - -function showMessage() { -*!* - let userName = "Bob"; // ローカル変数の宣言 -*/!* - - let message = 'Hello, ' + userName; // *!*Bob*/!* - alert(message); -} - -// 関数は作られ独自の userName を使います -showMessage(); - -alert( userName ); // *!*John*/!*, 変更されていません。関数は外部変数へアクセスしませんでした -``` - -```smart header="グローバル変数" -上のコードにおいて、外部の `userName` のような、任意の関数の外で宣言されている変数は *グローバル* と呼ばれます。 - -グローバル変数はどの関数からも見えます(ローカル変数により隠れていなければ)。 - -通常、関数はそれ自身のタスクのためのすべての変数を宣言します。また、グローバル変数にはプロジェクトレベルのデータのみを保持するため、どこからでも見える事が重要です。現代のコードはほとんどもしくは全くグローバル変数を持ちません。ほとんどの変数はその関数の中にあります。 -``` - -## パラメータ - -パラメータを使うことで、任意のデータを関数に渡すことができます(*関数の引数* と呼ばれます)。 - -下の例では、関数は2つのパラメータを持っています: `from` と `text` です。 - -```js run -function showMessage(*!*from, text*/!*) { // 引数: from, text - alert(from + ': ' + text); -} - -*!* -showMessage('Ann', 'Hello!'); // Ann: Hello! (*) -showMessage('Ann', "What's up?"); // Ann: What's up? (**) -*/!* -``` - -行 `(*)` と `(**)` で関数が呼ばれたとき、与えられた値はローカル変数 `from` と `text` にコピーされます。そして関数はそれらを使います。 - -ここにもう1つ例があります: 私たちは変数 `from` を持っており、それを関数に渡します。注意してください:関数は常に値のコピーを取得するため、関数は `from` を変更しますが、その変更は外には見えません。 - -```js run -function showMessage(from, text) { - -*!* - from = '*' + from + '*'; // "from" をより良く見せる -*/!* - - alert( from + ': ' + text ); -} - -let from = "Ann"; - -showMessage(from, "Hello"); // *Ann*: Hello - -// "from" の値は同じで、関数はローカルコピーを変更しました。 -alert( from ); // Ann -``` - -## Default 値 - -パラメータが与えられていない場合、その値は `undefined` になります。 - -例えば、前述の関数 `showMessage(from, text)` は1つの引数で呼ぶことも出来ます: - -```js -showMessage("Ann"); -``` - -それはエラーではありません。このような呼び出しは `"Ann: undefined"` を出力します。`text` がないので、`text === undefined` とみなされます。 - -"デフォルト" の `text` を使いたい場合は、このケースでは、`=` の後に指定することができます: - -```js run -function showMessage(from, *!*text = "no text given"*/!*) { - alert( from + ": " + text ); -} - -showMessage("Ann"); // Ann: no text given -``` - -これで、`text` パラメータが渡されていない場合、 値は `"no text given"` になります。 - -ここで、 `"no text given"` は文字列ですが、より複雑な式にすることもできます。そしてそれはパラメータが無い場合にのみ評価され代入されます。なので、これも可能です: - -```js run -function showMessage(from, text = anotherFunction()) { - // anotherFunction() はテキストが与えられなかった場合にのみ実行されます - // その結果がtextの値になります -} -``` - - -````smart header="デフォルトパラメータの古い形式" -javascriptの古いエディションはデフォルトパラメータをサポートしていませんでした。そのため、それらをサポートするための別の方法があります。これは殆どの古いスクリプトの中で見つけることができます。 - -例えば、`undefined` の明示的なチェック: - -```js -function showMessage(from, text) { -*!* - if (text === undefined) { - text = 'no text given'; - } -*/!* - - alert( from + ": " + text ); -} -``` - -...もしくは `||` 演算子: - -```js -function showMessage(from, text) { - // text が偽の場合、tetxt は "デフォルト" 値を取得します - text = text || 'no text given'; - ... -} -``` - - -```` - - -## 値の返却 - -関数は、実行結果として呼び出しコードに値を戻すことが出来ます。 - -最もシンプルな例は2つの値の合計を行う関数です: - -```js run no-beautify -function sum(a, b) { - *!*return*/!* a + b; -} - -let result = sum(1, 2); -alert( result ); // 3 -``` - -ディレクティブ `return` は関数の任意の場所に置くことが出来ます。もしも実行がそこに到達したとき、関数は停止し、値を呼び出しコードに返します(上の `result` へ代入します)。 - -1つの関数に多くの `return` が出現することもあります。例えば: - -```js run -function checkAge(age) { - if (age > 18) { -*!* - return true; -*/!* - } else { -*!* - return confirm('Got a permission from the parents?'); -*/!* - } -} - -let age = prompt('How old are you?', 18); - -if ( checkAge(age) ) { - alert( 'Access granted' ); -} else { - alert( 'Access denied' ); -} -``` - -値なしで `return` を使うことも出来ます。それは関数を直ぐに終了させます。 - -例: - -```js -function showMovie(age) { - if ( !checkAge(age) ) { -*!* - return; -*/!* - } - - alert( "Showing you the movie" ); // (*) - // ... -} -``` - -上のコードでは、`checkAge(age)` が `false` を返すと、`showMovie` `alert` の処理をしません。 - -````smart header="空の `return` または返却がないものは `undefined` を返します" -もしも関数が値を返却しない場合、`undefined` を返却した場合と同じです。: - -```js run -function doNothing() { /* empty */ } - -alert( doNothing() === undefined ); // true -``` - -空の `return` もまた `return undefined` と同じです: - -```js run -function doNothing() { - return; -} - -alert( doNothing() === undefined ); // true -``` -```` - -````warn header="`return`と値の間に改行を入れないでください" -`return` が長い式の場合、このように別の行に書くのが魅力的に見えるかもしれません: - -```js -return - (some + long + expression + or + whatever * f(a) + f(b)) -``` -JavaScriptは `return` の後にセミコロンを想定するため、これは動作しません。これは次と同じように動作します: - -```js -return*!*;*/!* - (some + long + expression + or + whatever * f(a) + f(b)) -``` -従って、それは事実上空の返却になります。なので、値は同じ行に置く必要があります。 -```` - -## 関数の命名 - -関数はアクションです。そのため、それらの名前は通常は動詞です。それは簡潔にすべきすが、関数がすることをできるだけ正確に表現してください。そして、コードを読む人は正しい手がかりを得られるようにします。 - -曖昧にアクションを記述する動詞のプレフィックスから関数を始めることは広く行われています。 -プレフィックスの意味についてチーム内での合意が必要です。 - -例えば、`"show"` で始まる関数は、通常何かを表示します。 - -以下で始まる関数... - -- `"get…"` -- 値を返します, -- `"calc…"` -- 何かを計算します, -- `"create…"` -- 何かを生成します, -- `"check…"` -- 何かをチェックし、真偽値を返します, etc - -このような名前の例です: - -```js no-beautify -showMessage(..) // メッセージを表示します -getAge(..) // 年齢を返します(なんとかしてその値を得る) -calcSum(..) // 合計を計算し、それを返します -createForm(..) // フォームを生成します(通常それを返却します) -checkPermission(..) // 権限をチェックし、true/false を返します -``` - -決まった位置にプレフィックスを使用すると、関数名を見ただけでそれがどのような種類の処理を行い、どのような値を返すのかを理解することが出来ます。 - -```smart header="1つの関数 -- 1つのアクション" -関数はその名前により提案されたことを正確にするべきです。 - -通常、2つの独立したアクションは、たとえそれらが一緒に呼ばれるとしても(その場合、私たちはそれらの2つを呼ぶ3つ目の関数を作ります)、2つの関数にするのが良いです。 - -このルールを破るいくつかの例です: - -- `getAge` -- 年齢とともに `警告` 表示すると悪くなります(取得のみをするべきです) -- `createForm` -- ドキュメントを変更してフォームを追加すると悪くなります(作成とその返却だけにするべきです) -- `checkPermission` -- `アクセス許可/拒否` のメッセージを表示すると悪くなります(チェックのみを実行し、その結果を返すべきです) - -これらの例はプレフィックスの共通の意味を前提としています。それらが意味することは、あなたとあなたのチームで決めるということです。恐らく、コードが異なる振る舞いをするのは普通なことです。しかし、プレフィックスが意味すること、プレフィックスの付いた関数ができること、できないことについてはしっかりとした理解をもっておくべきです。同じプレフィックスの関数はルールに従うべきです。そして、チームはその知識を共有するべきです。 -``` - -```smart header="究極短い関数名" -*非常に頻繁に* 使われる関数は、究極的に短い名前を持っていることがあります。 - -例えば、[jQuery](http://jquery.com) フレームワークは関数 `$` を定義しています。[LoDash](http://lodash.com/) ライブラリは、そのコアな関数として `_` を持っています。 - -それらは例外です。一般的に関数名は簡潔ではあるが、説明的でなければなりません。 -``` - -## 関数 == コメント - -関数は短く明確に1つのことを行うべきです。もしも関数が大きい場合、恐らく、それを幾つかの小さい関数に分ける価値があるでしょう。時々、このルールに従うことは簡単ではないかもしれませんが、間違いなく良いことです。 - -分割した関数はテストやデバッグが簡単になるだけでなく、 -- その存在が素晴らしいコメントです! - -例えば、下にある2つの関数 `showPrimes(n)`を比べてみましょう。どちらも[prime numbers](https://en.wikipedia.org/wiki/Prime_number)を `n` に達するまで出力します。 - -1つ目のパターンはラベルを使います: - -```js -function showPrimes(n) { - nextPrime: for (let i = 2; i < n; i++) { - - for (let j = 2; j < i; j++) { - if (i % j == 0) continue nextPrime; - } - - alert( i ); // a prime - } -} -``` - -2つ目のパターンは、素数の確認をするための追加の関数 `isPrime(n)` を使います。 - -```js -function showPrimes(n) { - - for (let i = 2; i < n; i++) { - *!*if (!isPrime(i)) continue;*/!* - - alert(i); // a prime - } -} - -function isPrime(n) { - for (let i = 2; i < n; i++) { - if ( n % i == 0) return false; - } - return true; -} -``` - -2つ目のパターンのほうが理解しやすいですね。コードの塊の代わりに、アクション(`isPrime`) の名前を見ます。このようなコードは *自己記述* と呼ばれる場合があります。 - -従って、関数はその再利用を意図していない場合でも作る事ができます。それらはコードを構造化し、読みやすくします。 - -## サマリ - -関数はこのように定義します: - -```js -function name(parameters, delimited, by, comma) { - /* code */ -} -``` - -- パラメータとして関数に渡される値は、ローカル変数にコピーされます。 -- 関数は外部変数にアクセスすることができます。しかし、それは内側からのみ動作します。関数の外側のコードは、関数のローカル変数を見ることは出来ません。 -- 関数は値を返すことができます。もしもそれをしなかった場合、その戻りは `undefined` です。 - -コードを綺麗で理解しやすいようにするために、その関数内では外部変数ではなく、主にローカル変数やパラメータを利用することを推奨します。 - -パラメータを取得するが副作用として外部変数を変更する関数よりも、パラメータを取得してそれらを処理し、結果を返す関数のほうが常に理解しやすいです。 - -関数名: - -- 名前は、関数がすることを明確に記述するべきです。コードの中で関数呼び出しを見るとき、良い名前であればそれが何をして何を返すのかを簡単に理解することができます。 -- 関数はアクションなので、関数名は通常動詞的です。 -- `create…`, `show…`, `get…`, `check…` など、数多くのよく知られた関数のプレフィックスが存在します。関数がすることのヒントとしてそれらを使いましょう。 - -関数はスクリプトの主な構成要素です。今や私たちは基礎をカバーしましたので、実際にそれらを作り、使い始めることが出来ます。しかし、それはまだ道のりの始まりに過ぎません。私たちは何度もそれらに戻り、より高度な機能について深めていきます。 diff --git a/1-js/02-first-steps/14-function-basics/function_basics.png b/1-js/02-first-steps/14-function-basics/function_basics.png deleted file mode 100644 index 7eb6e11e54..0000000000 Binary files a/1-js/02-first-steps/14-function-basics/function_basics.png and /dev/null differ diff --git a/1-js/02-first-steps/14-function-basics/function_basics@2x.png b/1-js/02-first-steps/14-function-basics/function_basics@2x.png deleted file mode 100644 index e629bd712c..0000000000 Binary files a/1-js/02-first-steps/14-function-basics/function_basics@2x.png and /dev/null differ diff --git a/1-js/02-first-steps/13-switch/1-rewrite-switch-if-else/solution.md b/1-js/02-first-steps/14-switch/1-rewrite-switch-if-else/solution.md similarity index 100% rename from 1-js/02-first-steps/13-switch/1-rewrite-switch-if-else/solution.md rename to 1-js/02-first-steps/14-switch/1-rewrite-switch-if-else/solution.md diff --git a/1-js/02-first-steps/13-switch/1-rewrite-switch-if-else/task.md b/1-js/02-first-steps/14-switch/1-rewrite-switch-if-else/task.md similarity index 100% rename from 1-js/02-first-steps/13-switch/1-rewrite-switch-if-else/task.md rename to 1-js/02-first-steps/14-switch/1-rewrite-switch-if-else/task.md diff --git a/1-js/02-first-steps/13-switch/2-rewrite-if-switch/solution.md b/1-js/02-first-steps/14-switch/2-rewrite-if-switch/solution.md similarity index 100% rename from 1-js/02-first-steps/13-switch/2-rewrite-if-switch/solution.md rename to 1-js/02-first-steps/14-switch/2-rewrite-if-switch/solution.md diff --git a/1-js/02-first-steps/13-switch/2-rewrite-if-switch/task.md b/1-js/02-first-steps/14-switch/2-rewrite-if-switch/task.md similarity index 100% rename from 1-js/02-first-steps/13-switch/2-rewrite-if-switch/task.md rename to 1-js/02-first-steps/14-switch/2-rewrite-if-switch/task.md diff --git a/1-js/02-first-steps/14-switch/article.md b/1-js/02-first-steps/14-switch/article.md new file mode 100644 index 0000000000..90e485b2eb --- /dev/null +++ b/1-js/02-first-steps/14-switch/article.md @@ -0,0 +1,172 @@ +# switch文 + +`switch` 文は複数の `if` チェックに置換できます。 + +これは値を複数のパターンと比較するための、よりわかりやすい方法を提供します。 + +## 構文 + +`switch` は1つ以上の `case` ブロックを持ち、 オプションで default を持ちます。 + +このようになります: + +```js no-beautify +switch(x) { + case 'value1': // if (x === 'value1') + ... + [break] + + case 'value2': // if (x === 'value2') + ... + [break] + + default: + ... + [break] +} +``` + +- `x` の値は、最初の `case` (それは `value1`)の値と厳密な等価のチェックをされます、そして2つ目(`value2`)と続きます。 +- 等価なものが見つかった場合、 `switch` は該当する `case` から始まるコードを実行し始めます。最も近い `break` まで(もしくは `switch` の終わりまで)。 +- マッチするケースが無い場合は、`default` コードが実行されます(存在する場合) + +## 例 + +`switch` の例です(実行されるコードはハイライトされています) + +```js run +let a = 2 + 2; + +switch (a) { + case 3: + alert( 'Too small' ); + break; +*!* + case 4: + alert( 'Exactly!' ); + break; +*/!* + case 5: + alert( 'Too large' ); + break; + default: + alert( "I don't know such values" ); +} +``` + +ここで、 `switch` は、最初の `case` である `3` から `a` との比較を始めます。マッチはしません。 + +そして `4` です。マッチするので、`case 4` から最も近い `break` までの実行を開始します。 + +**`break` がない場合、チェックなしで次の `case` の実行を継続します。** + +`break` なしの例です: + +```js run +let a = 2 + 2; + +switch (a) { + case 3: + alert( 'Too small' ); +*!* + case 4: + alert( 'Exactly!' ); + case 5: + alert( 'Too big' ); + default: + alert( "I don't know such values" ); +*/!* +} +``` + +上の例では、3つの `alert` が順に実行されるでしょう。 + +```js +alert( 'Exactly!' ); +alert( 'Too big' ); +alert( "I don't know such values" ); +``` + +````smart header="どのような式も `switch / case` の引数になります" +`switch` と `case` の両方は任意の表現が可能です。 + +例: + +```js run +let a = "1"; +let b = 0; + +switch (+a) { +*!* + case b + 1: + alert("this runs, because +a is 1, exactly equals b+1"); + break; +*/!* + + default: + alert("this doesn't run"); +} +``` +ここで `+a` は `1` が与えられ、 `case` で `b + 1` と比較されます。そして、対応するコードが実行されます。 +```` + +## "case"のグルーピング + +同じコードを共有する複数の `case` のパターンはグループ化できます。 + +たとえば、`case 3` と `case 5` で同じコードを実行したい場合: + +```js run no-beautify +let a = 2 + 2; + +switch (a) { + case 4: + alert('Right!'); + break; + +*!* + case 3: // (*) 2つのケースをグループ化 + case 5: + alert('Wrong!'); + alert("Why don't you take a math class?"); + break; +*/!* + + default: + alert('The result is strange. Really.'); +} +``` + +今、`3` と `5` は同じメッセージを表示します。 + +ケースを "グループ化" する機能は、`break` がない場合の `switch/case` の動作の副作用です。ここで `case 3` の実行は、`break` がないので `(*)` の行から始まり、`case 5` を通り抜けます。 + +## 型の問題 + +等価チェックは常に厳密であることに注目しましょう。マッチするために値は同じ型である必要があります。 + +たとえば、このコードを考えてみましょう: + +```js run +let arg = prompt("Enter a value?") +switch (arg) { + case '0': + case '1': + alert( 'One or zero' ); + break; + + case '2': + alert( 'Two' ); + break; + + case 3: + alert( 'Never executes!' ); + break; + default: + alert( 'An unknown value' ) +} +``` + +1. `0`, `1` の場合、最初の `alert` が実行されます。 +2. `2` の場合は2つ目の `alert` が実行されます。 +3. しかし `3` の場合、`prompt` の結果は文字列の `"3"`なので、数字の `3` との厳密な等価 `===` ではありません。そのため、`case 3` はデッドコードです! `default` ケースが実行されるでしょう。 diff --git a/1-js/02-first-steps/14-function-basics/1-if-else-required/solution.md b/1-js/02-first-steps/15-function-basics/1-if-else-required/solution.md similarity index 100% rename from 1-js/02-first-steps/14-function-basics/1-if-else-required/solution.md rename to 1-js/02-first-steps/15-function-basics/1-if-else-required/solution.md diff --git a/1-js/02-first-steps/14-function-basics/1-if-else-required/task.md b/1-js/02-first-steps/15-function-basics/1-if-else-required/task.md similarity index 100% rename from 1-js/02-first-steps/14-function-basics/1-if-else-required/task.md rename to 1-js/02-first-steps/15-function-basics/1-if-else-required/task.md diff --git a/1-js/02-first-steps/14-function-basics/2-rewrite-function-question-or/solution.md b/1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/solution.md similarity index 100% rename from 1-js/02-first-steps/14-function-basics/2-rewrite-function-question-or/solution.md rename to 1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/solution.md diff --git a/1-js/02-first-steps/14-function-basics/2-rewrite-function-question-or/task.md b/1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/task.md similarity index 100% rename from 1-js/02-first-steps/14-function-basics/2-rewrite-function-question-or/task.md rename to 1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/task.md diff --git a/1-js/02-first-steps/14-function-basics/3-min/solution.md b/1-js/02-first-steps/15-function-basics/3-min/solution.md similarity index 100% rename from 1-js/02-first-steps/14-function-basics/3-min/solution.md rename to 1-js/02-first-steps/15-function-basics/3-min/solution.md diff --git a/1-js/02-first-steps/14-function-basics/3-min/task.md b/1-js/02-first-steps/15-function-basics/3-min/task.md similarity index 100% rename from 1-js/02-first-steps/14-function-basics/3-min/task.md rename to 1-js/02-first-steps/15-function-basics/3-min/task.md diff --git a/1-js/02-first-steps/14-function-basics/4-pow/solution.md b/1-js/02-first-steps/15-function-basics/4-pow/solution.md similarity index 100% rename from 1-js/02-first-steps/14-function-basics/4-pow/solution.md rename to 1-js/02-first-steps/15-function-basics/4-pow/solution.md diff --git a/1-js/02-first-steps/15-function-basics/4-pow/task.md b/1-js/02-first-steps/15-function-basics/4-pow/task.md new file mode 100644 index 0000000000..651733fa70 --- /dev/null +++ b/1-js/02-first-steps/15-function-basics/4-pow/task.md @@ -0,0 +1,20 @@ +importance: 4 + +--- + +# 関数 pow(x,n) + + +`x` の累乗 `n` を返す関数 `pow(x,n)` を書いてください。 つまり、 `x` をそれ自身で `n` 回掛け、その結果を返します。 + +```js +pow(3, 2) = 3 * 3 = 9 +pow(3, 3) = 3 * 3 * 3 = 27 +pow(1, 100) = 1 * 1 * ...*1 = 1 +``` + +`x` と `n` を聞くプロンプトをもつ web ページを作り、`pow(x,n)` の結果を表示しなさい。 + +[demo] + +P.S. このタスクでは、`n` に自然数のみをサポートします。 diff --git a/1-js/02-first-steps/15-function-basics/article.md b/1-js/02-first-steps/15-function-basics/article.md new file mode 100644 index 0000000000..f85034e2a8 --- /dev/null +++ b/1-js/02-first-steps/15-function-basics/article.md @@ -0,0 +1,498 @@ +# 関数 + +スクリプトの色々な場所で同じアクションを実行する必要がある場合がよくあります。 + +例えば、訪問者がログイン/ログアウトしたり、また複数の箇所で見栄の良いメッセージを表示する必要があったりします。 + +関数はプログラムのメインの "構成要素" です。これによりコードを繰り返すことなく何度も呼び出すことができます。 + +私たちは既に組み込み関数の例を見ています。 `alert(message)`, `prompt(message, default)` や `confirm(question)`です。これと同じように私たち自身も関数を作ることができます。 + +## 関数定義 + +関数を作るために、*関数定義* を使います。 + +次のようになります: + +```js +function showMessage() { + alert( 'Hello everyone!' ); +} +``` + +`function` キーワードが最初にきて、次に *関数名* がきます、そして括弧の中に *パラメータ* のリスト(カンマ区切り、上の例では空)がきて、最後に中括弧の間に関数のコード、 "関数本体" です。 + +```js +function name(parameters) { + ...body... +} +``` + +作成した関数はその関数名で呼び出すことができます: `showMessage()` + +例: + +```js run +function showMessage() { + alert( 'Hello everyone!' ); +} + +*!* +showMessage(); +showMessage(); +*/!* +``` + +`showMessage()` の呼び出しは、関数のコードを実行します。この例では、2度メッセージが表示されます。 + +この例は関数のメインの目的の1つを明確に示しています: コードの複製を回避する、と言うことです。 + +もしメッセージ内容、または表示方法を変更する必要がある場合、1箇所のコード(関数)を修正するだけで十分です。 + +## ローカル変数 + +関数内で定義された変数は、関数内でのみ参照可能です。 + +例: + +```js run +function showMessage() { +*!* + let message = "Hello, I'm JavaScript!"; // ローカル変数 +*/!* + + alert( message ); +} + +showMessage(); // Hello, I'm JavaScript! + +alert( message ); // <-- エラー! 変数は関数のローカルです +``` + +## 外部変数 + +関数は外部変数にアクセスすることもできます。次の例を見てください: + +```js run no-beautify +let *!*userName*/!* = 'John'; + +function showMessage() { + let message = 'Hello, ' + *!*userName*/!*; + alert(message); +} + +showMessage(); // Hello, John +``` + +関数は外部変数に対してフルアクセス権を持ち、変更することもできます。 + +例: + +```js run +let *!*userName*/!* = 'John'; + +function showMessage() { + *!*userName*/!* = "Bob"; // (1) 外部変数の変更 + + let message = 'Hello, ' + *!*userName*/!*; + alert(message); +} + +alert( userName ); // 関数呼び出しの前は *!*John*/!* + +showMessage(); + +alert( userName ); // *!*Bob*/!*, 関数によって値が変更されました +``` + +外部の変数は、同じ名前のローカル変数が存在しない場合にのみ使われます。そのため、`let` を忘れた場合、意図せず外部の変数を変更してしまう可能性があります。 + +同じ名前の変数が関数内に宣言されている場合は、外部変数を *隠します*。例えば、以下のコードでは関数はローカルの `userName` を使います。外部の `userName` は無視されます。 + +```js run +let userName = 'John'; + +function showMessage() { +*!* + let userName = "Bob"; // ローカル変数の宣言 +*/!* + + let message = 'Hello, ' + userName; // *!*Bob*/!* + alert(message); +} + +// 関数は作られ独自の userName を使います +showMessage(); + +alert( userName ); // *!*John*/!*, 変更されていません。関数は外部変数へアクセスしませんでした +``` + +```smart header="グローバル変数" +上のコードにおいて、外部の `userName` のような、関数の外で宣言されている変数は *グローバル* と呼ばれます。 + +グローバル変数はどの関数からも見えます(ローカル変数により隠れていなければ)。 + +通常、関数は自身のタスクに必要なすべての変数を宣言します。また、グローバル変数にはプロジェクトレベルのデータのみを保持するため、どこからでも見える事が重要です。現代のコードはほとんどもしくは全くグローバル変数を持ちません。ほぼすべての変数は関数に属します。 +``` + +## パラメータ + +パラメータを使うことで、任意のデータを関数に渡すことができます。 + +下の例では、関数は2つのパラメータを持っています: `from` と `text` です。 + +```js run +function showMessage(*!*from, text*/!*) { // 引数: from, text + alert(from + ': ' + text); +} + +*!* +showMessage('Ann', 'Hello!'); // Ann: Hello! (*) +showMessage('Ann', "What's up?"); // Ann: What's up? (**) +*/!* +``` + +行 `(*)` と `(**)` で関数が呼ばれたとき、与えられた値はローカル変数 `from` と `text` にコピーされます。そして関数はそれらを使います。 + +ここにもう1つ例があります: 私たちは変数 `from` を持っており、それを関数に渡します。注意してください:関数は常に値のコピーを取得するため、関数の中の処理は `from` を変更していますが、その変更は外には見えません: + + +```js run +function showMessage(from, text) { + +*!* + from = '*' + from + '*'; // "from" をより良く見せる +*/!* + + alert( from + ': ' + text ); +} + +let from = "Ann"; + +showMessage(from, "Hello"); // *Ann*: Hello + +// "from" の値は同じで、関数はローカルコピーを変更しています。 +alert( from ); // Ann +``` + +関数のパラメータとして渡された値は "引数(ひきすう)" とも呼ばれます。 +これらの用語を整理すると: + +- パラメータとは、関数の宣言時に括弧内に記述される変数のこと(宣言時の用語) +- 引数とは、関数が呼び出されたときに渡される値のこと(呼び出し時の用語) + +パラメータを列挙して関数を宣言し、引数を渡して関数を呼び出すことになります。 + +上の例ではこのように言えるでしょう。「関数showMessageは2つのパラメータを持つと宣言されており、from と "Hello" という2つの引数を与えて呼び出されている」 + +## デフォルト値 + +関数の呼び出し時に引数が与えられていない場合、対応する値は `undefined` になります。 + +例えば、前述の関数 `showMessage(from, text)` は1つの引数で呼ぶことも出来ます: + +```js +showMessage("Ann"); +``` + +それはエラーではありません。このような呼び出しは `"*Ann*: undefined"` を出力します。`text` が渡されていないため、`text` は `undefined` となります。 + +パラメータのいわゆる "デフォルト" (呼び出し時に省略された場合に使用される)値を、関数宣言の中で `=` を使用して指定することが可能です: + +```js run +function showMessage(from, *!*text = "no text given"*/!*) { + alert( from + ": " + text ); +} + +showMessage("Ann"); // Ann: no text given +``` + +これで `text` パラメータが渡されていない場合、 値は `"no text given"` になります。 + +ここで、 `"no text given"` は文字列ですが、より複雑な式にすることもできます。そしてそれはパラメータが無い場合にのみ評価され、代入されます。なので、このようなことも可能です: + +```js run +function showMessage(from, text = anotherFunction()) { + // anotherFunction() はテキストが与えられなかった場合にのみ実行されます + // その結果がtextの値になります +} +``` + +```smart header="デフォルト値の評価" +JavaScriptでは、デフォルト値はそれぞれのパラメータが与えられずに関数が呼び出されるたびに評価されます。 + +上の例だと `anotherFunction()` は、 `text` のパラメータが与えられずに `showMessage()` が呼び出されるたびに実行されます。 +``` + +### 代替のデフォルトパラメータ + +パラメータのデフォルト値を関数宣言ではなく、後の段階で実行中に設定することが理にかなっている場合があります。 + +省略されたパラメータをチェックするために、`undefined` と比較できます: + +```js run +function showMessage(text) { +*!* + if (text === undefined) { + text = 'empty message'; + } +*/!* + + alert(text); +} + +showMessage(); // empty message +``` + +...もしくは `||` 演算子: + +```js +// パラメータが省略 or "" の場合, 'empty' を設定 +function showMessage(text) { + text = text || 'empty'; + ... +} +``` + +モダンな JavaScript エンジンは [NULL合体演算子](info:nullish-coalescing-operator) `??` をサポートしており、`0` などの偽値を通常とみなす場合に適しています: + +```js run +// count パラメータがない場合は "unknown" +function showCount(count) { + alert(count ?? "unknown"); +} + +showCount(0); // 0 +showCount(null); // unknown +showCount(); // unknown +``` + +## 値の返却 + +関数は、実行結果として呼び出しコードに値を戻すことが出来ます。 + +最もシンプルな例は2つの値の合計を行う関数です: + +```js run no-beautify +function sum(a, b) { + *!*return*/!* a + b; +} + +let result = sum(1, 2); +alert( result ); // 3 +``` + +ディレクティブ `return` は関数の任意の場所に置くことが出来ます。もしも実行がそこに到達したとき、関数は停止し、値を呼び出し元のコードに返します(上の `result` へ代入します)。 + +1つの関数に多くの `return` が出現することもあります。例えば: + +```js run +function checkAge(age) { + if (age > 18) { +*!* + return true; +*/!* + } else { +*!* + return confirm('Got a permission from the parents?'); +*/!* + } +} + +let age = prompt('How old are you?', 18); + +if ( checkAge(age) ) { + alert( 'Access granted' ); +} else { + alert( 'Access denied' ); +} +``` + +値なしで `return` を使うことも出来ます。これは関数を直ぐに終了させます。 + +例: + +```js +function showMovie(age) { + if ( !checkAge(age) ) { +*!* + return; +*/!* + } + + alert( "Showing you the movie" ); // (*) + // ... +} +``` + +上のコードでは、`checkAge(age)` が `false` を返すと、`showMovie` は `alert` の処理をしません。 + +````smart header="空の `return`、 または返却がないものは `undefined` を返します" +関数が値を返却しない場合、それは `undefined` を返却した場合と同じになります。: + +```js run +function doNothing() { /* empty */ } + +alert( doNothing() === undefined ); // true +``` + +空の `return` もまた `return undefined` と同じです: + +```js run +function doNothing() { + return; +} + +alert( doNothing() === undefined ); // true +``` +```` + +````warn header="`return`と値の間に改行を入れないでください" +`return` が長い式の場合、このように別の行に書くのが魅力的に見えるかもしれません: + +```js +return + (some + long + expression + or + whatever * f(a) + f(b)) +``` +JavaScriptは `return` の後にセミコロンを想定するため、これは動作しません。これは次と同じように動作します: + +```js +return*!*;*/!* + (some + long + expression + or + whatever * f(a) + f(b)) +``` + +従って、これは事実上空の返却になります。なので、値は同じ行に置く必要があります。 + +もし複数行にまたがった式を返却したい場合は、`return` と同じ行から開始する必要があります。あるいは、少なくとも次のように開始括弧を置きます: + +```js +return ( + some + long + expression + + or + + whatever * f(a) + f(b) + ) +``` +これは期待する通りに動作するでしょう。 +```` + +## 関数の命名 + +関数はアクションです。そのため、それらの名前は通常は動詞です。それは簡潔にすべきですが、関数がすることをできるだけ正確に表現してください。そして、コードを読む人が正しい手がかりを得られるようにします。 + +曖昧なアクションを示す動詞のプレフィックスから関数名を始めることは広く行われています。プレフィックスの意味についてはチーム内での合意が必要です。 + +例えば、`"show"` で始まる関数は、通常何かを表示します。 + +以下で始まる関数... + +- `"get…"` -- 値を返します, +- `"calc…"` -- 何かを計算します, +- `"create…"` -- 何かを生成します, +- `"check…"` -- 何かをチェックし、真偽値を返します, etc + +このような名前の例です: + +```js no-beautify +showMessage(..) // メッセージを表示します +getAge(..) // 年齢を返します(なんとかしてその値を得る) +calcSum(..) // 合計を計算し、それを返します +createForm(..) // フォームを生成します(通常それを返却します) +checkPermission(..) // 権限をチェックし、true/false を返します +``` + +決まった位置にプレフィックスを使用すると、関数名を見ただけでそれがどのような種類の処理を行い、どのような値を返すのかを理解することが出来ます。 + +```smart header="1つの関数 -- 1つのアクション" +関数はその名前により提案されたことを正確にするべきです。 + +通常、2つの独立したアクションは、たとえそれらが一緒に呼ばれるとしても、2つの関数にするのが良いです(その場合は、通常その2つを呼ぶ3つ目の関数を作ります)。 + +このルールを破るいくつかの例です: + +- `getAge` -- 年齢を取得するとともに `警告` を表示します(取得のみをするべきです) +- `createForm` -- フォームを作成して、ドキュメントに追加します(作成とその返却だけにするべきです) +- `checkPermission` -- `アクセス許可/拒否` のメッセージを表示するのは良くありません(チェックを実行し、その結果を返すのみにすべきです) + +これらの例はプレフィックスの共通の意味を前提としています。これらが意味することは、あなたとあなたのチームで前提を決めるということです。恐らく、コードが異なる振る舞いをするのは普通なことです。しかし、プレフィックスが意味すること、プレフィックスの付いた関数ができること、できないことについてはしっかりとした理解をもっておくべきです。同じプレフィックスの関数はルールに従うべきです。そして、チームはそれを共有するべきです。 +``` + +```smart header="究極的に短い関数名" +*非常に頻繁に* 使われる関数は、究極的に短い名前を持っていることがあります。 + +例えば、[jQuery](http://jquery.com) フレームワークは関数 `$` を定義しています。[LoDash](http://lodash.com/) ライブラリは、そのコアな関数として `_` を持っています。 + +それらは例外です。一般的に関数名は簡潔で説明的でなければなりません。 +``` + +## 関数 == コメント + +関数は短く明確に1つのことを行うべきです。もし関数が大きい場合、恐らくそれを幾つかの小さい関数に分けることは価値があるでしょう。このルールに従うことは簡単ではないこともありますが、間違いなく良いことです。 + +分割した関数はテストやデバッグが簡単になるだけでなく、 -- その存在自体が素晴らしいコメントになります! + +例えば、下にある2つの関数 `showPrimes(n)`を比べてみましょう。どちらも[素数](https://en.wikipedia.org/wiki/Prime_number)を `n` に達するまで出力します。 + +1つ目のパターンはラベルを使います: + +```js +function showPrimes(n) { + nextPrime: for (let i = 2; i < n; i++) { + + for (let j = 2; j < i; j++) { + if (i % j == 0) continue nextPrime; + } + + alert( i ); // a prime + } +} +``` + +2つ目のパターンは、素数の確認をするための追加の関数 `isPrime(n)` を使います。 + +```js +function showPrimes(n) { + + for (let i = 2; i < n; i++) { + *!*if (!isPrime(i)) continue;*/!* + + alert(i); // a prime + } +} + +function isPrime(n) { + for (let i = 2; i < n; i++) { + if ( n % i == 0) return false; + } + return true; +} +``` + +2つ目のパターンのほうが理解しやすいですね。コードの塊の代わりに、アクション(`isPrime`) の名前を見ます。このようなコードは *自己記述的* と呼ばれる場合があります。 + +従って、関数はその再利用を意図していない場合でも作ることがあります。それらはコードを構造化し、読みやすくします。 + +## サマリ + +関数はこのように定義します: + +```js +function name(parameters, delimited, by, comma) { + /* code */ +} +``` + +- パラメータとして関数に渡される値は、ローカル変数にコピーされます。 +- 関数は外部の変数にアクセスすることができます。しかし、それは内側からのみ機能します。関数の外側のコードは、関数のローカル変数を見ることはできません。 +- 関数は値を返すことができます。もしもそれをしなかった場合、戻り値は `undefined` です。 + +コードを綺麗で理解しやすいようにするために、その関数内では外部変数ではなく、ローカル変数やパラメータを利用することを推奨します。 + +パラメータを取得せずに外部変数を変更する関数よりも、パラメータを取得してそれを処理して結果を返す関数の方が、常に理解しやすいものです。 + +関数名: + +- 名前は、関数がすることを明確に記述するべきです。コードの中で関数呼び出しを見るとき、良い名前であればそれが何をして何を返すのかを簡単に理解することができます。 +- 関数はアクションなので、関数名は通常動詞的です。 +- `create…`, `show…`, `get…`, `check…` など、数多くのよく知られた関数のプレフィックスが存在します。関数がすることのヒントとしてそれらを使いましょう。 + +関数はスクリプトの主な構成要素です。今や私たちは基礎をカバーしたので、実際にそれらを作り使い始めることができます。しかし、それはまだほんの始まりに過ぎません。私たちは何度もそれらに戻り、より高度な機能について深めていきます。 diff --git a/1-js/02-first-steps/15-function-expressions-arrows/article.md b/1-js/02-first-steps/15-function-expressions-arrows/article.md deleted file mode 100644 index 9634c1e0b1..0000000000 --- a/1-js/02-first-steps/15-function-expressions-arrows/article.md +++ /dev/null @@ -1,478 +0,0 @@ -# 関数式とアロー - -JavaScriptでは、関数は "魔法の言語構造" ではなく、特別な種類の値です。 - -[cut] - -以前私たちが使っているた構文は *関数宣言* と呼ばれます: - -```js -function sayHi() { - alert( "Hello" ); -} -``` - -*関数式* と呼ばれる、関数を作るための別の項分があります。 - -それはこのようになります: - -```js -let sayHi = function() { - alert( "Hello" ); -}; -``` - -ここで、他の任意の値と同じように、関数は作られ明示的に変数に代入されています。 -どのように変数が定義されても、それは単に変数 `sayHi` に格納される値です。 - -これらのコード例の意味は同じです: "関数を作成し、変数 `sayHi` にそれを置きます" - -`alert` を使ってその値を出力することもできます: - -```js run -function sayHi() { - alert( "Hello" ); -} - -*!* -alert( sayHi ); // 関数のコードが表示されます -*/!* -``` - -`sayHi` の後に括弧がないので、最後の行は関数が実行されないことに注意してください。関数名への言及がその実行を引き起こすプログラミング言語も存在しますが、JavaScriptはそうではありません。 - -JavaScriptでは、関数は値です。そのため、それを値として扱うことができます。上のコードはその文字列表現を表示し、それはソースコードです。 - -`sayHi()` のように呼ぶことができる点で、もちろんそれは特別な値です。 - -しかし、それはまだ値なので他の種類の値のようにそれを扱うことができます。 - -関数を別の変数にコピーすることが出来ます: - -```js run no-beautify -function sayHi() { // (1) 作成 - alert( "Hello" ); -} - -let func = sayHi; // (2) コピー - -func(); // Hello // (3) コピーの実行(動きます)! -sayHi(); // Hello // これもまだ動きます(なぜでしょう?) -``` - -上記で起こっていることの詳細は次の通りです: - -1. 関数宣言 `(1)` で関数を生成し、変数名 `sayHi` に置きます。 -2. 行 `(2)` でそれを変数 `func` にコピーします。 - - 改めて注意してください:`sayHi` の後に括弧はありません。もしそれがあった場合、`sayHi` の *関数自身* ではなく、`func = sayHi()` は `sayHi()` の呼び出しの結果を `func` に書き込みます。 -3. 今や、関数は `sayHi()` と `func()` 両方で呼ぶことができます。 - -私たちは、1行目で、`sayHi` を宣言するために関数式を使うこともできます: - -```js -let sayHi = function() { ... }; - -let func = sayHi; -// ... -``` - -すべて同じように動作します。さらに何が起こっているのか、より明白ですね。 - - -````smart header="なぜ末尾にセミコロンがあるのでしょう?" -疑問があるかもしれません。なぜ関数式は末尾にセミコロン `;` を持つのか、そして関数宣言にはそれがないのか: - -```js -function sayHi() { - // ... -} - -let sayHi = function() { - // ... -}*!*;*/!* -``` - -答えはシンプルです: -- コードブロックや `if { ... }`, `for { }`, `function f { }` などの構文構造の末尾に `;` が必要ありません。 -- 関数式は文の内側で使われます: `let sayHi = ...;` を値として利用します。それはコードブロックではありません。セミコロン `;` はどんな値であれ文の最後に推奨されています。そのため、ここのセミコロンは関数式自体になんら関係しません、単に文の終わりです。 -```` - -## コールバック関数 - -値として関数を渡し、関数式を使う例をみてみましょう。 - -私たちは、3つのパラメータを持つ関数 `ask(question, yes, no)` を書きます: - -`question` -: 質問のテキスト - -`yes` -: もしも答えが "はい" の場合に実行する関数 - -`no` -: もしも答えが "いいえ" の場合に実行する関数 - -関数は `question` を訪ね、ユーザの回答に合わせて、`yes()` または `no()` を呼びます: - -```js run -*!* -function ask(question, yes, no) { - if (confirm(question)) yes() - else no(); -} -*/!* - -function showOk() { - alert( "You agreed." ); -} - -function showCancel() { - alert( "You canceled the execution." ); -} - -// 使用法: 関数 showOk, showCancel は ask の引数として渡されます -ask("Do you agree?", showOk, showCancel); -``` - -これに関して、より短く記述する方法を探す前に、ブラウザ(といくつかのサーバサイドのケース)では、このような関数はかなり普及していることに留意しましょう。実際の実装と上の例の主な違いは、実際の関数は単純な `confirm` よりも、ユーザとやり取りするためにより複雑な方法を利用することです。ブラウザでは、このような関数は、通常良く見える質問ウィンドウに描画されます。しかしそれは別の話です。 - -**`ask`の引数は *コールバック関数* または単に *コールバック* と呼ばれます。** - -この考え方は、私たちは関数を渡し、それがもし必要であれば後で "呼び戻す" ことを期待します。このケースでは、`showOK` は "はい" のためのコールバックになり、`showCancel` は "いいえ" の回答のためです。 - -同じ関数をより短く書くために関数式を使うことが出来ます: - -```js run no-beautify -function ask(question, yes, no) { - if (confirm(question)) yes() - else no(); -} - -*!* -ask( - "Do you agree?", - function() { alert("You agreed."); }, - function() { alert("You canceled the execution."); } -); -*/!* -``` - -ここでは、関数は `ask(...)` 呼び出しの中で正しく宣言されています。それらは名前を持たないので、*無名* と呼ばれます。 -このような関数は `ask` の外側からアクセスできません(変数に割り当てられていないため)、しかしそれはここで欲しいものです。 - -このようなコードは我々のスクリプトでとても自然に表れます。それは JavaScript の精神に基づいています。 - -```smart header="関数は \"アクション\" を表す値です" -文字列や数値のような通常の値は *データ* を表します。 - -関数は *アクション* として認識されます。 - -私たちは、変数間でそれを渡し、必要な時に実行させることができます。 -``` - - -## 関数式 vs 関数宣言 - -関数宣言と関数式の違いを明確に述べてみましょう。 - -まず、構文です: - -- *関数宣言:* メインのコードフローで別の文として宣言された関数 - - ```js - // 関数宣言 - function sum(a, b) { - return a + b; - } - ``` -- *関数式:* 式の内部、または別の構文構造の中で作れらた関数 - - ここで、関数は "代入式 =" の右側に作成されます。: - ```js - // 関数式 - let sum = function(a, b) { - return a + b; - }; - ``` - -よりささいな違いは、関数がJavaScriptエンジンによって *作られたとき* です。 - -**関数式は、実行がそれに到達した時に作られ、それ以降、利用可能になります。** - -一度実行フローが代入 `let sum = function…` の右辺へ渡ったら -- 関数は作られ、そこから使えるようになります(代入や呼び出しなど)。 - -関数宣言は異なります - -**関数宣言はスクリプト/コードブロック全体で使用できます。** - -言い換えると、JavaScriptがスクリプトまたはコードブロックの実行の準備をする時、最初にその中の関数定義を探し、関数を生成します。私たちはそれを "初期化段階" として考えることが出来ます。 - -そして、全ての関数宣言が処理されたあと、実行が続けられます。 - -結果的に、関数宣言として宣言された関数は、定義された関数よりも早く呼ぶことができます。 - -例えば、これは動作します: - -```js run refresh untrusted -*!* -sayHi("John"); // Hello, John -*/!* - -function sayHi(name) { - alert( `Hello, ${name}` ); -} -``` - -関数宣言 `sayHi` は、JavaScriptがスクリプトの開始の準備をしているときに生成され、その中でどこからでも見えます。 - -...もしもそれが関数式だった場合、動作しないでしょう: - -```js run refresh untrusted -*!* -sayHi("John"); // エラー! -*/!* - -let sayHi = function(name) { // (*) no magic any more - alert( `Hello, ${name}` ); -}; -``` - -関数式は、実行がそれに到達した時に作られます。それは行 `(*)` でのみ起こります。遅すぎます。 - -**関数宣言がコードブロックの中で作られるとき、そのブロックの内側であればどこからでも見えます。しかし、その外側からは見えません。** - -必要とされるブロックの中だけでローカル変数を宣言することは、時には便利です。しかし、その機能も問題を引き起こす可能性があります。 - -例えば、私たちがランタイムの中で得た `age` 変数に依存する関数 `welcome()` を宣言する必要があるとしましょう。そして、しばらくしてから使用することを予定します。 - -下のコードはうまく動作しません: - -```js run -let age = prompt("What is your age?", 18); - -// 条件付きで関数を宣言する -if (age < 18) { - - function welcome() { - alert("Hello!"); - } - -} else { - - function welcome() { - alert("Greetings!"); - } - -} - -// ...後でそれを使う -*!* -welcome(); // エラー: welcome は未定義です -*/!* -``` - -なぜなら、関数宣言は、それが存在するコードブロックの内側でのみ見えるからです。 - -別の例です: - -```js run -let age = 16; // 例として16 - -if (age < 18) { -*!* - welcome(); // \ (実行) -*/!* - // | - function welcome() { // | - alert("Hello!"); // | 関数宣言はそれが宣言されたブロックの中であれば - } // | どこでも利用可能です - // | -*!* - welcome(); // / (実行) -*/!* - -} else { - - function welcome() { // age = 16 の場合, この "welcome" は決して作られません - alert("Greetings!"); - } -} - -// ここは、波括弧の外です -// なのでその中で作られた関数宣言は見ることができません - -*!* -welcome(); // エラー: welcome は定義されていません -*/!* -``` - -`if` の外側で `welcome` を見えるようにするためにはどうしたらよいでしょうか? - -正しいアプローチは、関数式を使い、`welcome` を `if` の外で宣言され、適切なスコープをもつ変数に代入することです。 - -これは、意図したとおりに動作します: - -```js run -let age = prompt("What is your age?", 18); - -let welcome; - -if (age < 18) { - - welcome = function() { - alert("Hello!"); - }; - -} else { - - welcome = function() { - alert("Greetings!"); - }; - -} - -*!* -welcome(); // ok now -*/!* -``` - -もしくは、疑問符演算子 `?` を使うことでさらにシンプルにできます: - -```js run -let age = prompt("What is your age?", 18); - -let welcome = (age < 18) ? - function() { alert("Hello!"); } : - function() { alert("Greetings!"); }; - -*!* -welcome(); // ok now -*/!* -``` - - -```smart header="関数宣言と関数式のどちらを選択するのか?" -経験則として、私たちが関数を宣言する必要があるとき、最初に考えるのは、以前私たちが使った関数宣言構文です。私たちはこれらの関数が宣言される前に呼ぶことができるため、コードを体系化する自由度が増します。 - -また、コードの中で、`let f = function(…) {…}` よりも `function f(…) {…}` の方が調べるのが少し簡単です。関数宣言はより "目を引きます"。 - -...しかし、関数宣言が幾つかの理由で適していない場合(上でみた例)、関数式が使われるべきです。 -``` - - -## アロー関数 - -関数を作成ための、より非常にシンプルで簡潔な構文がもう1つあります。それはしばしば関数式よりも優れています。これは "アロー関数" と呼ばれ、次のようになります: - -```js -let func = (arg1, arg2, ...argN) => expression -``` - -...これは引数 `arg1..argN` をもち、右側でそれらを使用する `expression` を評価し、その結果を返す関数 `func` を作ります。 - -言い換えると、次のコードと概ね一緒です: - -```js -let func = function(arg1, arg2, ...argN) { - return expression; -} -``` - -...が、はるかに簡潔です - -例を見てみましょう: - -```js run -let sum = (a, b) => a + b; - -/* アロー関数は次の形式のより短い形です: - -let sum = function(a, b) { - return a + b; -}; -*/ - -alert( sum(1, 2) ); // 3 - -``` - -もしも引数が1つだけの場合、括弧は省略可能なので、さらに短くできます: - -```js run -// 次と同じです -// let double = function(n) { return n * 2 } -*!* -let double = n => n * 2; -*/!* - -alert( double(3) ); // 6 -``` - -もしも引数がない場合、括弧は必須で、空白にします: - -```js run -let sayHi = () => alert("Hello!"); - -sayHi(); -``` - -アロー関数は、関数式として同じ方法で使用できます。 - -例えば、ここでは `welcome()` の例を再び書きます: - -```js run -let age = prompt("What is your age?", 18); - -let welcome = (age < 18) ? - () => alert('Hello') : - () => alert("Greetings!"); - -welcome(); // ok now -``` - -アロー関数は、最初は馴染みが無く、読みにくいように見えるかもしれませんが、構造に慣れるとすぐに変わります。 - -多くの文字を書くのが面倒なとき、シンプルなワンライナーの処理を書くときにはとても便利です。 - -```smart header="複数行のアロー関数" - -上の例は、`=>` の左から引数を取得し、右側の式を評価しました。 - -複数の式や文のようにもう少し複雑なものが必要な時があります。それもまた可能ですが、この場合は括弧で囲む必要があります。そして、その中で通常の `return` を使います。 - -このようになります: - -```js run -let sum = (a, b) => { // 波括弧を使って複数行の関数を書けます - let result = a + b; -*!* - return result; // 波括弧を使った場合、結果を得るには return を使います -*/!* -}; - -alert( sum(1, 2) ); // 3 -``` - -```smart header="他にもあります" -ここでは、簡潔にするためにアロー関数を賞賛しました。しかし、それだけではありません!!アロー関数は他にも興味深い機能を持っています。後ほどチャプター で触れます。 - -現時点では、既にワンライナーの処理やコールバックのためにそれらを使うことが出来ます。 -``` - -## サマリ - -- 関数は値です。それらは、コード上のどの場所でも、割り当てたり、コピーしたり、宣言することができます。 -- もしも、関数がメインのコードフローの中で別の文として宣言されていたら、それは "関数宣言" と呼ばれます。 -- もしも関数が式の一部として作られたら、それは "関数式" と呼ばれます。 -- 関数宣言は、コードブロックが実行される前に処理されます。それらはブロックの中ではどこからでも見えます。 -- 関数式は、実行フローがそれに到達した時に作られます。 - -ほとんどのケースでは、関数の宣言が必要な場合、関数宣言が望ましいです。なぜなら、それ自身の宣言の前で見えるためです。これにより、コード構成の柔軟性が増し、通常は読みやすくなります。 - -従って、関数宣言がそのタスクに適さない場合にのみ関数式を使うべきです。このチャプターで幾つかの例を見て来ました、そして今後もっと見ていくでしょう。 - -アロー関数はワンライナーのために便利です。それらは2つの旨味があります: - -1. 括弧無し: `(...args) => expression` -- 右側は式です: 関数はそれを評価しその結果を返します。 -2. 括弧あり: `(...args) => { body }` -- 括弧があると、関数内で複数の文を書くことができます、しかし何かを返却する場合には、明確に `return` が必要です。 diff --git a/1-js/02-first-steps/16-function-expressions/article.md b/1-js/02-first-steps/16-function-expressions/article.md new file mode 100644 index 0000000000..8537f038eb --- /dev/null +++ b/1-js/02-first-steps/16-function-expressions/article.md @@ -0,0 +1,368 @@ +# 関数式 + +JavaScriptでは、関数は "魔法の言語構造" ではなく、特別な種類の値です。 + +前に私たちが使っていた構文は *関数宣言* と呼ばれます: + +```js +function sayHi() { + alert( "Hello" ); +} +``` + +これとは別に、*関数式* と呼ばれる、関数を作るための別の構文があります。 + +それはこのようになります: + +```js +let sayHi = function() { + alert( "Hello" ); +}; +``` + +ここでは、関数は他の任意の値と同じように明示的に変数に代入されています。どのように関数が定義されても、それは単に変数 `sayHi` に格納される値です。 + +これらのコード例の意味は同じです: "関数を作成し、変数 `sayHi` にそれを格納します" + +`alert` を使ってその値を出力することもできます: + +```js run +function sayHi() { + alert( "Hello" ); +} + +*!* +alert( sayHi ); // 関数のコードが表示されます +*/!* +``` + +`sayHi` の後に括弧がないので、最後の行は関数は実行されないことに注意してください。関数名への言及がその実行となるプログラミング言語も存在しますが、JavaScriptはそうではありません。 + +JavaScriptでは、関数は値です。そのため、それを値として扱うことができます。上のコードはその文字列表現を表示します(それはソースコードです)。 + +`sayHi()` のように呼ぶことができる点で、もちろんそれは特別な値です。 + +しかし、それは値なので、他のタイプの値のように扱うことができます。 + +関数を別の変数にコピーすることができます: + +```js run no-beautify +function sayHi() { // (1) 作成 + alert( "Hello" ); +} + +let func = sayHi; // (2) コピー + +func(); // Hello // (3) コピーの実行(動きます)! +sayHi(); // Hello // これもまだ動きます(なぜでしょう?) +``` + +上で起こっていることの詳細は次の通りです: + +1. 関数宣言 `(1)` で関数を生成し、変数名 `sayHi` に格納します。 +2. 行 `(2)` でそれを変数 `func` にコピーします。 + + 改めて注意してください:`sayHi` の後に括弧はありません。もし括弧があった場合、`sayHi` の *関数自身* ではなく、`func = sayHi()` は `sayHi()` の呼び出し結果を `func` に書き込みます。 +3. これで、関数は `sayHi()` と `func()` どちらでも呼ぶことができます。 + +また、1行目で `sayHi` を宣言するのに関数式を使うこともできます: + +```js +let sayHi = function() { ... }; + +let func = sayHi; +// ... +``` + +すべて同じように動作します。何が起こっているのかより明白ですね。 + + +````smart header="なぜ末尾にセミコロンがあるのでしょう?" +疑問があるかもしれません。なぜ関数式は末尾にセミコロン `;` を持つのか、そして関数宣言にはそれがないのか: + +```js +function sayHi() { + // ... +} + +let sayHi = function() { + // ... +}*!*;*/!* +``` + +答えはシンプルです: +- コードブロックや `if { ... }`, `for { }`, `function f { }` などの構文構造の末尾には `;` が必要ありません。 +- 関数式は文の内側で使われます: `let sayHi = ...;` の値として利用します。これはコードブロックではありません。セミコロン `;` はどんな値であれ文の最後に推奨されています。従って、ここのセミコロンは関数式自体と関係はなく、単に文の終わりです。 +```` + +## コールバック関数 + +値として関数を渡し、関数式を使う例をみてみましょう。 + +私たちは、3つのパラメータを持つ関数 `ask(question, yes, no)` を書きます: + +`question` +: 質問内容 + +`yes` +: 答えが "はい" の場合に実行する関数 + +`no` +: 答えが "いいえ" の場合に実行する関数 + +関数は `question` を聞き、ユーザの回答に合わせて、`yes()` または `no()` を呼びます: + +```js run +*!* +function ask(question, yes, no) { + if (confirm(question)) yes() + else no(); +} +*/!* + +function showOk() { + alert( "You agreed." ); +} + +function showCancel() { + alert( "You canceled the execution." ); +} + +// 使用法: 関数 showOk, showCancel は ask の引数として渡されます +ask("Do you agree?", showOk, showCancel); +``` + +これをもっと簡単に書く方法を探る前に、ブラウザ(と場合によってはサーバ側)では、このような関数は非常に一般的であること留意しましょう。実際の実装と上の例の主な違いは、実際の関数は単純な `confirm` よりも、より複雑な方法でユーザとやり取りをすることです。ブラウザでは、通常このような関数は見栄えのよい質問ウィンドウを描画します。が、それはまた別の話です。 + +**`ask`の引数の `showOk` と `showCancel` は *コールバック関数* または単に *コールバック* と呼ばれます。** + +このアイデアは、渡した関数が必要に応じて後から "コールバック" されることを期待するというものです。このケースでは、`showOK` は "はい" のためのコールバック関数になり、`showCancel` は "いいえ" の回答のためのコールバック関数です。 + +同じ関数をより短く書くために関数式を使うことができます: + +```js run no-beautify +function ask(question, yes, no) { + if (confirm(question)) yes() + else no(); +} + +*!* +ask( + "Do you agree?", + function() { alert("You agreed."); }, + function() { alert("You canceled the execution."); } +); +*/!* +``` + +ここでは、関数は `ask(...)` 呼び出しの中で正しく宣言されています。これらは名前を持たないので *無名関数* と呼ばれます。このような関数は、変数に割り当てられていないため `ask` の外側からはアクセスできませんが、ここでは私たちにとってちょうどよいものとなっています。 + +このようなコードはスクリプトの中で自然に現れます。それは JavaScript の精神に基づいています。 + +```smart header="関数は \"アクション\" を表す値です" +文字列や数値のような通常の値は *データ* を現します。 + +関数は *アクション* として認識されます。 + +変数間で渡し、必要な時に実行させることができます。 +``` + + +## 関数式 vs 関数宣言 + +関数宣言と関数式の違いを明確に述べてみましょう。 + +まず、構文です: + +- *関数宣言:* メインのコードフローで別の文として宣言された関数 + + ```js + // 関数宣言 + function sum(a, b) { + return a + b; + } + ``` +- *関数式:* 式の内部、または別の構文構造の中で作れらた関数。ここでは、関数は "代入式" `=` の右側で作られます: + + ```js + // 関数式 + let sum = function(a, b) { + return a + b; + }; + ``` + +よりささいな違いは、関数がJavaScriptエンジンによって *作られたとき* です。 + +**関数式は、実行がそれに到達した時に作られ、それ以降で利用可能になります。** + +一度実行フローが代入 `let sum = function…` の右辺へ渡ったら -- 関数は作られ、そこから使えるようになります(代入や呼び出しなど)。 + +関数宣言は異なります + +**関数宣言はスクリプト/コードブロック全体で使用できます。** + +つまり、JavaScriptがスクリプトまたはコードブロックの実行の準備をする時、最初にその中の関数定義を探し、関数を生成します。それは "初期化段階" と考えることができます。 + +そして、すべての関数宣言が処理されたあと、実行が続けられます。 + +結果的に、関数宣言として宣言された関数は、関数が定義されている場所よりも前で呼ぶことができます。 + +例えば、これは動作します: + +```js run refresh untrusted +*!* +sayHi("John"); // Hello, John +*/!* + +function sayHi(name) { + alert( `Hello, ${name}` ); +} +``` + +関数宣言 `sayHi` は、JavaScriptがスクリプトの開始の準備をしているときに生成され、その中でどこからでも見えます。 + +...もしもそれが関数式だった場合、動作しないでしょう: + +```js run refresh untrusted +*!* +sayHi("John"); // エラー! +*/!* + +let sayHi = function(name) { // (*) no magic any more + alert( `Hello, ${name}` ); +}; +``` + +関数式は、実行がそれに到達した時に作られます。それは行 `(*)` で起こります。遅すぎます。 + +**関数宣言がコードブロックの中で作られるとき、そのブロックの内側であればどこからでも見えます。しかし、その外側からは見えません。** + +必要とされるブロックの中だけでローカル変数を宣言することは、時には便利です。しかし、その機能も問題を引き起こす可能性があります。 + +例えば、ランタイムの中で得た `age` 変数に依存する関数 `welcome()` を宣言する必要があるとしましょう。そして、しばらくしてから使用する予定だとします。 + +下のコードはうまく動作しません: + +```js run +let age = prompt("What is your age?", 18); + +// 条件付きで関数を宣言する +if (age < 18) { + + function welcome() { + alert("Hello!"); + } + +} else { + + function welcome() { + alert("Greetings!"); + } + +} + +// ...後で使う +*!* +welcome(); // エラー: welcome は未定義です +*/!* +``` + +なぜなら、関数宣言は、それが存在するコードブロックの内側でのみ見えるからです。 + +別の例です: + +```js run +let age = 16; // 例として16 + +if (age < 18) { +*!* + welcome(); // \ (実行) +*/!* + // | + function welcome() { // | + alert("Hello!"); // | 関数宣言はそれが宣言されたブロックの中であれば + } // | どこでも利用可能です + // | +*!* + welcome(); // / (実行) +*/!* + +} else { + + function welcome() { // age = 16 の場合, この "welcome" は決して作られません + alert("Greetings!"); + } +} + +// ここは、波括弧の外です +// なのでその中で作られた関数宣言は見ることができません + +*!* +welcome(); // エラー: welcome は定義されていません +*/!* +``` + +`if` の外側で `welcome` を見えるようにするためにはどうしたらよいでしょうか? + +正しいアプローチは、関数式を使い、`welcome` を `if` の外で宣言し、適切なスコープをもつ変数に代入することです。 + +これは、意図したとおりに動作します: + +```js run +let age = prompt("What is your age?", 18); + +let welcome; + +if (age < 18) { + + welcome = function() { + alert("Hello!"); + }; + +} else { + + welcome = function() { + alert("Greetings!"); + }; + +} + +*!* +welcome(); // ok now +*/!* +``` + +もしくは、疑問符演算子 `?` を使うことでさらにシンプルにできます: + +```js run +let age = prompt("What is your age?", 18); + +let welcome = (age < 18) ? + function() { alert("Hello!"); } : + function() { alert("Greetings!"); }; + +*!* +welcome(); // ok now +*/!* +``` + + +```smart header="関数宣言と関数式のどちらを選択するのか?" +経験則として、関数を宣言する必要があるとき、最初に考えるのは関数宣言構文です。関数が宣言される前に呼ぶことができるため、コードを体系化する自由度が増します。 + +また、コードの中で、`let f = function(…) {…}` よりも `function f(…) {…}` の方が調べるのが少し簡単です。関数宣言はより "目を引きます"。 + +...しかし、関数宣言が幾つかの理由で適していない場合(上でみた例)、関数式を使用するべきです。 +``` + +## サマリ + +- 関数は値です。それらはコード上のどの場所でも、割り当て、コピー、宣言をすることができます。 +- 関数がメインのコードフローの中で別の文として宣言されていたら、それは "関数宣言" と呼ばれます。 +- 関数が式の一部として作られたら、それは "関数式" と呼ばれます。 +- 関数宣言は、コードブロックが実行される前に処理されます。ブロックの中ではどこからでも見えます。 +- 関数式は、実行フローがそれに到達した時に作られます。 + +たいていのケースでは、関数の宣言が必要な場合、関数宣言が望ましいです。なぜなら、それ自身の宣言の前でも利用することができるからです。これにより、コード構成の柔軟性が増し、通常は読みやすくなります。 + +従って、関数宣言がそのタスクに適さない場合にのみ関数式を使うべきです。この章ではそのような例をいくつか見てきましたが、今後もさらに多くの例を見ていくことになるでしょう。 diff --git a/1-js/02-first-steps/16-javascript-specials/article.md b/1-js/02-first-steps/16-javascript-specials/article.md deleted file mode 100644 index b28b063549..0000000000 --- a/1-js/02-first-steps/16-javascript-specials/article.md +++ /dev/null @@ -1,287 +0,0 @@ -# JavaScript 特別授業 - -このチャプターでは、微妙なケースに特に注意を払いながら、私たちが今まで学んだJavaScriptの機能を簡単に再確認します。 - -[cut] - -## コード構造 - -文はセミコロンで区切られます: - -```js run no-beautify -alert('Hello'); alert('World'); -``` - -通常、行の終わりは区切りとして扱われますので、これは動作します: - -```js run no-beautify -alert('Hello') -alert('World') -``` - -これは "自動セミコロン挿入" と呼ばれます。ときどき、これは動作しません。例えば: - -```js run -alert("There will be an error after this message") - -[1, 2].forEach(alert) -``` - -ほとんどのコードスタイルのガイドは、各文の後にセミコロンを置くことに賛同しています。 - -セミコロンはコードブロック `{...}` や、ループのような構文構造の後では必要ありません: - -```js -function f() { - // 関数宣言のあとにセミコロンは不要です -} - -for(;;) { - // ループの後にセミコロンは不要です -} -``` - -...しかし、"余分な" セミコロンを任意の場所に置いたとしても、それはエラーではありません。それは無視されます。 - -より詳細はこちら: . - -## Strict モード - -現在のJavaScriptのすべての機能を完全に有効にするには、`"use strict"` でスクリプトを始める必要があります。 - -```js -'use strict'; - -... -``` - -そのディレクティブはスクリプトの先頭、もしくは関数の最初である必要があります。 - -`"use strict"` がなくても動作しますが、幾つかの機能が古いスタイル, "互換の方法" で振る舞います。私たちは一般的に現代の振る舞いを好みます。 - -より詳細はこちら: . - -## 変数 - -これらを使って定義できます: - -- `let` -- `const` (定数, 変更できない) -- `var` (古いスタイル, あとで見ます) - -変数の名前は次を含むことができます: -- 文字と数字、しかし1文字目に数字は指定できません。 -- 文字 `$` と `_` は普通の文字です。 -- 非ラテンのアルファベットや象形文字も使えますが、一般的には使用されません。 - -変数は動的に型付けされます。 彼らは任意の値を格納することができます: - -```js -let x = 5; -x = "John"; -``` - -7つのデータ型があります: - -- `number` 浮動少数点と整数値両方 -- `string` 文字列 -- `boolean` 論理値: `true/false` -- `null` -- 単一の値 `null` を持つ型。"空白", "存在しない" を意味する -- `undefined` -- 単一の値 `undefined` を持つ型。"未割り当て" を意味する -- `object` と `symbol` -- 複雑なデータ構造やユニークな識別子で、私たちはまだそれらは学んでないです。 - -`typeof` 演算子は値の型を返します。2つ例外があります: -```js -typeof null == "object" // 言語の間違い -typeof function(){} == "function" // 関数は特別に扱われます -``` - -より詳細はこちらです: and . - -## インタラクション - -私たちは動作環境としてブラウザを使っているので、基本のUI関数は次の通りです: - -[`prompt(question[, default])`](mdn:api/Window/prompt) -: `question` を訪ね、訪問者の入力もしくは、"cancel" が選択されたときは `null` を返します。 - -[`confirm(question)`](mdn:api/Window/confirm) -: `question` を訪ね、OKとキャンセルを選択することを提案します。選択は `true/false` として返却されます。 - -[`alert(message)`](mdn:api/Window/alert) -: `message` を出力します。 - -それらの関数はすべて *モーダル* であり、コードの実行を止め、訪問者が回答するまで、そのページとのやり取りを防ぎます。 - -例えば: - -```js run -let userName = prompt("Your name?", "Alice"); -let isTeaWanted = confirm("Do you want some tea?"); - -alert( "Visitor: " + userName ); // Alice -alert( "Tea wanted: " + isTeaWanted ); // true -``` - -より詳細はこちらです: . - -## 演算子 - -JavaScriptは次のような演算子をサポートします: - -算術 -: 常連: `* + - /`, また剰余として `%`、数字の累乗として `**`。 - - バイナリプラス `+` は文字列を連結します。また、オペランドのいずれかが文字列であれば、もう一方も文字列に変換されます: - - ```js run - alert( '1' + 2 ); // '12', string - alert( 1 + '2' ); // '12', string - ``` - -代入 -: シンプルな代入があります: `a = b` と `a *= 2` のような組み合わせです。 - -ビット単位 -: ビット演算子ビットレベルで整数を扱います。必要なときに、[docs](mdn:/JavaScript/Reference/Operators/Bitwise_Operators)を見てください。 - -三元 -: 3つのパラメータを持つ唯一の演算子です: `cond ? resultA : result B`. もしも `cond` が真の場合、`resultA` を返し、そうでなければ `resultB` を返します。 - -論理演算子 -: 論理積 `&&` と 論理和 `||` は短絡評価を行い、それが停止したところの値を返します。 - -比較 -: 異なる型の値のための等価チェック `==` は、それらを数値に変換します(`null` と `undefined`を除きます。それらは、お互いに等しく、他とは等しくなりません)、従って、これらは等価です。: - - ```js run - alert( 0 == false ); // true - alert( 0 == '' ); // true - ``` - - 他の比較も同様に数値に変換します。 - - 厳密等価演算子 `===` は変換を行いません: 異なる型は常に異なる値を意味します。: - - 値 `null` と `undefined` は特別です: それらはお互いに等価 `==` であり、それ以外と等しくありません。 - - より大きい/少ない演算子は文字列を1文字ずつ比較し、他の型は数値に変換します。 - -論理演算子 -: カンマ演算子のような、他のものはほとんどありません。 - -より詳細はこちらです: , , . - -## ループ - -- 私たちは3つのタイプのループを説明しました: - - ```js - // 1 - while (condition) { - ... - } - - // 2 - do { - ... - } while (condition); - - // 3 - for(let i = 0; i < 10; i++) { - ... - } - ``` - -- `for(let...)` ループの中で宣言された変数はループの内側でのみ見えます。しかし、`let` を省略することができ、既存の変数を再利用することも出来ます。 -- ディレクティブ `break/continue` はループ全体/現在のイテレーションを終了させることができます。ネストされたループを停止する場合にはラベルを使ってください。 - -詳細はこちらです: . - -今後、オブジェクトを扱うためのより多くのループのタイプを学びます。 - -## "switch" 構造 - -"switch" 構造は複数の `if` チェックを置換できます。それは比較に `===` を使います。 - -例えば: - -```js run -let age = prompt('Your age?', 18); - -switch (age) { - case 18: - alert("Won't work"); // プロンプトの結果は文字列であり、数値ではありません - - case "18": - alert("This works!"); - break; - - default: - alert("Any value not equal to one above"); -} -``` - -詳細はこちらです: . - -## 関数 - -私たちは、JavaScriptで関数を作る3つの方法をカバーしました。: - -1. 関数宣言: メインコードフローの中の関数 - - ```js - function sum(a, b) { - let result = a + b; - - return result; - } - ``` - -2. 関数式: 式のコンテキストにある関数 - - ```js - let sum = function(a, b) { - let result = a + b; - - return result; - } - ``` - - `sum = function name(a, b)` のように、関数式は名前を持つことができます、しかしその `name` は関数の内側でのみ見えます。 - -3. アロー関数: - - ```js - // 右側の式です - let sum = (a, b) => a + b; - - // もしくは { ... } を使った複数行の構文で、return が必要です: - let sum = (a, b) => { - // ... - return a + b; - } - - // 引数なし - let sayHi = () => alert("Hello"); - - // 1つの引数 - let double = n => n * 2; - ``` - - -- 関数はローカル変数を持ちます: それらはその関数本体の中で宣言されます。このような変数は関数の中でだけ見えます。 -- パラメータはデフォルト値を持つことが出来ます。: `function sum(a = 1, b = 2) {...}`. -- 関数は常に何かを返します。もしも `return` 文がない場合は、`undefined` を返します。 - - -| 関数宣言 | 関数式 | -|----------------------|---------------------| -| 全体のコードブロックで見える| 実行がそこに到達したときに作られる | -| - | 名前を持つことができますが、関数の内側でのみ見えます。 | - -詳細はこちら: , . - -## これからが本番です - -ここまではJavaScriptの機能の簡単な一覧でした。今のところ、私たちは基本だけを学びました。このチュートリアルではさらに、JavaScriptのより特別で高度な機能についても説明していきます。 diff --git a/1-js/02-first-steps/15-function-expressions-arrows/1-rewrite-arrow/solution.md b/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/solution.md similarity index 100% rename from 1-js/02-first-steps/15-function-expressions-arrows/1-rewrite-arrow/solution.md rename to 1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/solution.md diff --git a/1-js/02-first-steps/15-function-expressions-arrows/1-rewrite-arrow/task.md b/1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/task.md similarity index 100% rename from 1-js/02-first-steps/15-function-expressions-arrows/1-rewrite-arrow/task.md rename to 1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/task.md diff --git a/1-js/02-first-steps/17-arrow-functions-basics/article.md b/1-js/02-first-steps/17-arrow-functions-basics/article.md new file mode 100644 index 0000000000..9006138193 --- /dev/null +++ b/1-js/02-first-steps/17-arrow-functions-basics/article.md @@ -0,0 +1,111 @@ +# アロー関数の基本 + +関数を作成するための、よりシンプルで簡潔な構文がもう1つあります。それはしばしば関数式よりも優れています。 + +これは "アロー関数" と呼ばれ、次のようになります: + +```js +let func = (arg1, arg2, ...argN) => expression +``` + +...これは引数 `arg1..argN` を取り、それらを使用する `expression` を評価し、その結果を返す関数 `func` を作ります。 + +言い換えると、次のコードと概ね一緒です: + +```js +let func = function(arg1, arg2, ...argN) { + return expression; +} +``` + +具体的な例を見てみましょう: + +```js run +let sum = (a, b) => a + b; + +/* アロー関数は次よりも短い形式です: + +let sum = function(a, b) { + return a + b; +}; +*/ + +alert( sum(1, 2) ); // 3 +``` + +ご覧の通り、`(a, b) => a + b` は `a` と `b` 、2つの引数を受け取る関数を意味します。実行時に、`a + b` を評価し、結果を返します。 + +- 引数が1つだけの場合、括弧は省略可能なので、さらに短くできます: + + 例: + + ```js run + *!* + let double = n => n * 2; + // おおよそこちらと同じ: let double = function(n) { return n * 2 } + */!* + + alert( double(3) ); // 6 + ``` + +- 引数がない場合、空の括弧が必須です: + + ```js run + let sayHi = () => alert("Hello!"); + + sayHi(); + ``` + +アロー関数は、関数式として同じ方法で使用できます。 + +例えば、ここでは `welcome()` の例を再び書きます: + +```js run +let age = prompt("What is your age?", 18); + +let welcome = (age < 18) ? + () => alert('Hello') : + () => alert("Greetings!"); + +welcome(); // ok now +``` + +アロー関数は、最初は馴染みが無く、読みにくいように見えるかもしれませんが、構造に慣れるとすぐに変わります。 + +シンプルなワンライナーの処理で、多くの文字を書くのが面倒なときにはとても便利です。 + +## 複数行のアロー関数 + +上の例は、`=>` の左から引数を取得し、右側の式を評価しました。 + +複数の式や文のように、もう少し複雑なものが必要な時があります。それも可能ですが、この場合は波括弧で囲む必要があります。そして、その中で通常の `return` を使います。 + +このようになります: + +```js run +let sum = (a, b) => { // 波括弧を使って複数行の関数を書けます + let result = a + b; +*!* + return result; // 波括弧を使う場合、明示的な return が必要です +*/!* +}; + +alert( sum(1, 2) ); // 3 +``` + +```smart header="他にもあります" +ここでは、簡潔にするためにアロー関数を賞賛しました。しかし、それだけではありません!! + +アロー関数は他にも興味深い機能を持っています。 + +これらを学ぶためには、最初に JavaScript の他の側面について知る必要があります。なので、後ほどチャプター で触れます。 + +今の時点で、我々はすでにワンライナーの処理やコールバック処理のためにアロー関数を使うことができます。 +``` + +## サマリ + +アロー関数はワンライナーに対し便利です。2つの種類があります: + +1. 波括弧無し: `(...args) => expression` -- 右側は式です: 関数はそれを評価しその結果を返します。 +2. 波括弧あり: `(...args) => { body }` -- 括弧があると、関数内で複数の文を書くことができます、しかし何かを返却する場合には、明示的な `return` が必要です。 diff --git a/1-js/02-first-steps/18-javascript-specials/article.md b/1-js/02-first-steps/18-javascript-specials/article.md new file mode 100644 index 0000000000..39131e0045 --- /dev/null +++ b/1-js/02-first-steps/18-javascript-specials/article.md @@ -0,0 +1,283 @@ +# JavaScript スペシャル(これまでのおさらい) + +このチャプターでは、微妙なケースに注意を払いながら、私たちが今まで学んだJavaScriptの機能を簡単に再確認します。 + +## コード構造 + +文はセミコロンで区切られます: + +```js run no-beautify +alert('Hello'); alert('World'); +``` + +通常、行の終わりは区切りとして扱われますので、これは動作します: + +```js run no-beautify +alert('Hello') +alert('World') +``` + +これは "自動セミコロン挿入" と呼ばれます。ときどき、これは動作しません。例えば: + +```js run +alert("There will be an error after this message") + +[1, 2].forEach(alert) +``` + +ほとんどのコードスタイルのガイドは、各文の後にセミコロンを置くことに賛同しています。 + +セミコロンはコードブロック `{...}` や、ループのような構文構造の後では必要ありません: + +```js +function f() { + // 関数宣言のあとにセミコロンは不要です +} + +for(;;) { + // ループの後にセミコロンは不要です +} +``` + +...しかし、"余分な" セミコロンを任意の場所に置いたとしても、それはエラーではありません。それは無視されます。 + +より詳細はこちら: . + +## Strict モード + +現在のJavaScriptのすべての機能を完全に有効にするには、`"use strict"` でスクリプトを始める必要があります。 + +```js +'use strict'; + +... +``` + +そのディレクティブはスクリプトの先頭、もしくは関数の最初である必要があります。 + +`"use strict"` がなくてもすべて動作しますが、幾つかの機能は "互換性のある" 旧来の振る舞いとなります。一般的に、現代的な動作が好まれるでしょう。 + +言語に最新の機能のいくつか(今後学ぶクラスなど)は暗黙的に strict モードを有効にします。 + +より詳細はこちら: . + +## 変数 + +これらを使って定義できます: + +- `let` +- `const` (定数, 変更できない) +- `var` (古いスタイル, あとで見ます) + +変数の名前は次を含むことができます: +- 文字と数字、しかし1文字目に数字は指定できません。 +- 記号の `$` と `_` は普通の文字と同等です。 +- 非ラテンのアルファベットや象形文字も使えますが、一般的には使用されません。 + +変数は動的に型付けされます。 それらは任意の値を格納することができます: + +```js +let x = 5; +x = "John"; +``` + +7つのデータ型があります: + +- `number` 浮動少数点と整数値両方 +- `bigint` 任意の長さの整数値 +- `string` 文字列 +- `boolean` 論理値: `true/false` +- `null` -- 単一の値 `null` を持つ型。"空", "存在しない" を意味する +- `undefined` -- 単一の値 `undefined` を持つ型。"未割り当て" を意味する +- `object` と `symbol` -- 複雑なデータ構造やユニークな識別子です。私たちはまだそれらは学んでいません。 + +`typeof` 演算子は値の型を返します。2つ例外があります: +```js +typeof null == "object" // 言語の間違い +typeof function(){} == "function" // 関数は特別に扱われます +``` + +より詳細はこちらです: and . + +## インタラクション + +私たちは動作環境としてブラウザを使っているので、基本のUI関数は次の通りです: + +[`prompt(question[, default])`](mdn:api/Window/prompt) +: `question` を尋ね、訪問者が入力した内容を返すか、"cancel" がクリックされたときは `null` を返します。 + +[`confirm(question)`](mdn:api/Window/confirm) +: `question` を尋ね、OKとキャンセルのどちらかを選択するように提案します。選択された結果は `true/false` として返されます。 + +[`alert(message)`](mdn:api/Window/alert) +: `message` を出力します。 + +それらの関数はすべて *モーダル* であり、コードの実行を止め、訪問者が回答するまでそのページとのやり取りを防ぎます。 + +例えば: + +```js run +let userName = prompt("Your name?", "Alice"); +let isTeaWanted = confirm("Do you want some tea?"); + +alert( "Visitor: " + userName ); // Alice +alert( "Tea wanted: " + isTeaWanted ); // true +``` + +より詳細はこちらです: . + +## 演算子 + +JavaScriptは次のような演算子をサポートします: + +算術 +: 通常の四則演算の `* + - /`、また剰余として `%`、冪乗として `**`。 + + 二項演算子プラス `+` は文字列を連結します。また、オペランドのいずれかが文字列であれば、もう一方も文字列に変換されます: + + ```js run + alert( '1' + 2 ); // '12', string + alert( 1 + '2' ); // '12', string + ``` + +代入 +: 単純な代入の `a = b` と `a *= 2` のような他の演算子と組み合わせたものがあります。 + +ビット単位 +: ビット演算子はビットレベルで整数を扱います。必要なときに、[docs](mdn:/JavaScript/Reference/Operators/Bitwise_Operators)を見てください。 + +3項 +: 3つのパラメータを持つ唯一の演算子です: `cond ? resultA : result B`. `cond` が真の場合、`resultA` を返し、そうでなければ `resultB` を返します。 + +論理演算子 +: 論理積 `&&` と 論理和 `||` は短絡評価を行い、それが停止したところの値(`true`/`false` である必要はありません)を返します。論理否定 `!` はオペランドをブール型に変換し、その逆の値を返します。 + +NULL合体演算子 +: `??` 演算子は変数のリストから定義済みの値を選択する方法として提供されています。`a ?? b` の結果は `a` が `null/undefined` の場合は `b`、そうでなければ `a` です。 + +比較 +: 異なる型の値のための等価チェック `==` は、それらを数値に変換します(`null` と `undefined`を除きます。それらは、お互いに等しく、他とは等しくなりません)。従って以下は等価です。: + + ```js run + alert( 0 == false ); // true + alert( 0 == '' ); // true + ``` + + 他の比較も同様に数値に変換します。 + + 厳密等価演算子 `===` は変換を行いません: 異なる型は常に異なる値を意味します。 + + 値 `null` と `undefined` は特別です: それらはお互いに等価 `==` であり、それ以外と等しくありません。 + + より大きい/少ない演算子は文字列を1文字ずつ比較し、他の型は数値に変換します。 + +その他 +: 他にもカンマ演算子などがあります。 + +より詳細はこちらです: , , . + +## ループ + +- 私たちは3つのタイプのループを説明しました: + + ```js + // 1 + while (condition) { + ... + } + + // 2 + do { + ... + } while (condition); + + // 3 + for(let i = 0; i < 10; i++) { + ... + } + ``` + +- `for(let...)` ループの中で宣言された変数はループの内側でのみ見えます。しかし、`let` を省略することができ、既存の変数を再利用することも出来ます。 +- ディレクティブ `break/continue` はループ全体/現在のイテレーションを終了させることができます。ネストされたループを停止する場合にはラベルを使ってください。 + +詳細はこちらです: . + +今後、オブジェクトを扱うためのより多くの種類のループを学びます。 + +## "switch" 構造 + +"switch" 構造は複数の `if` チェックに置換できます。それは比較に `===` を使います。 + +例えば: + +```js run +let age = prompt('Your age?', 18); + +switch (age) { + case 18: + alert("Won't work"); // プロンプトの結果は文字列であり、数値ではありません + + case "18": + alert("This works!"); + break; + + default: + alert("Any value not equal to one above"); +} +``` + +詳細はこちらです: . + +## 関数 + +私たちは、JavaScriptで関数を作る3つの方法をカバーしました。: + +1. 関数宣言: メインコードフローの中の関数 + + ```js + function sum(a, b) { + let result = a + b; + + return result; + } + ``` + +2. 関数式: 式のコンテキストにある関数 + + ```js + let sum = function(a, b) { + let result = a + b; + + return result; + } + ``` + +3. アロー関数: + + ```js + // 右側の式です + let sum = (a, b) => a + b; + + // もしくは { ... } を使った複数行の構文で、return が必要です: + let sum = (a, b) => { + // ... + return a + b; + } + + // 引数なし + let sayHi = () => alert("Hello"); + + // 1つの引数 + let double = n => n * 2; + ``` + + +- 関数はローカル変数を持ちます: それらはその関数本体の中で宣言されます。このような変数は関数の中でだけ見えます。 +- パラメータはデフォルト値を持つことが出来ます。: `function sum(a = 1, b = 2) {...}`. +- 関数は常に何かを返します。もしも `return` 文がない場合は `undefined` を返します。 + +詳細はこちら: , . + +## これからが本番です + +ここまではJavaScriptの機能の簡単な一覧でした。今のところ、私たちは基本だけを学びました。このチュートリアルではさらに、JavaScriptのより特別で高度な機能について説明していきます。 diff --git a/1-js/03-code-quality/01-debugging-chrome/article.md b/1-js/03-code-quality/01-debugging-chrome/article.md index 9156ad30a8..df0232f737 100644 --- a/1-js/03-code-quality/01-debugging-chrome/article.md +++ b/1-js/03-code-quality/01-debugging-chrome/article.md @@ -2,77 +2,75 @@ より複雑なコードを書く前に、デバッグについて話しましょう。 -すべてのモダンブラウザとその他のほとんどの環境は "デバッグ" をサポートしています。 -- 開発ツールの特別なUIで、エラーの発見と修正がはるかに簡単になります。 +[デバッギング](https://en.wikipedia.org/wiki/Debugging)はスクリプト内のエラーを見つけ、修正するプロセスです。すべてのモダンブラウザと他の環境のほとんどはデバッギングツール(デバッグを簡単に行えるようにする開発者ツールのUI)をサポートしています。また、コードをステップ毎に追跡して正確に起きていることを確認することもできます。 -この観点では恐らく最も機能が充実してるので、ここではChromeを使います。 +ここでは、恐らくこの観点では最も機能が充実している Chrome を使います。 -[cut] +## "sources" パネル -## "sources" ペイン - -Chromeのバージョンによって少し違って見えるかもしれませんが、そこが何であるかは明白でしょう。 +Chromeのバージョンによっては少し違って見えるかもしれませんが、何があるかは明白でしょう。 - Chromeの [example page](debugging/index.html) を開きます。 - `key:F12` (Mac: `key:Cmd+Opt+I`) で開発者ツールをONにします。 -- `source` ペインを選択します。 +- `source` パネルを選択します。 -もしもこの画面を見るのが初めての場合見るべきものがあります: +この画面を見るのが初めてであれば、見ておくべきものがあります: -![](chrome-open-sources.png) +![](chrome-open-sources.svg) トグルボタン はファイルを表示するタブを開きます。 + それをクリックして、`index.html` 、次にツリービューの `hello.js` を選択しましょう。ここで表示される内容は次の通りです: -![](chrome-tabs.png) +![](chrome-tabs.svg) ここでは3つの領域が確認できます: -1. **リソース領域** html、JavaScript, css や、ページに紐付いているイメージなどを含む他のファイルがリストされます。 -2. **ソース領域** ソースコードを表示します。 -3. **情報と制御領域** これはデバッグのためで、後ほどすぐにそれを見ていきます。 +1. **ファイルナビゲータ** ペインには、HTML、JavaScript、CSSや、ページに紐付いているイメージなどを含むファイルがリストされます。Chromeの拡張機能もここに表示されることがあります。 +2. **コードエディタ** ペインは、ソースコードを表示します。 +3. **JavaScript デバッギング** ペインはデバッグのためのもので、この後見ていきます。 -同じトグル を再びクリックすることで、リソースのリストやコードを隠すことができます。 +同じトグル を再びクリックすること、リソースの一覧やコードを隠すことができます。 ## コンソール -`Esc` を押すとコンソールが下に表示されます。そこでコマンドを入力し、`key:Enter` で実行することが出来ます。 +`Esc` を押すとコンソールが下に表示されます。そこでコマンドを入力し、`key:Enter` を押すとコマンドを実行することができます。 -文が実行された後、その結果は下に表示されます。 +実行結果は下に表示されます。 -例えば、ここでは `1+2 ` は `3` になり、`hello("debugger")` は何も返さないので、その結果は `undefined` です: +例えば、ここでは `1+2 ` は `3` になり、`hello("debugger")` は何も返さないので、結果は `undefined` です: -![](chrome-sources-console.png) +![](chrome-sources-console.svg) ## ブレイクポイント -[example page](debugging/index.html) のコード内で何が起こっているのか見てみましょう。`hello.js` で、行番号 `4` をクリックします。コードではなく、`"4"` の数字の右です。 +[example page](debugging/index.html) のコードの中で何が起こっているのか見てみましょう。`hello.js` で、行番号 `4` をクリックします。コードではなく、左側にある数字の `"4"` です。 -おめでとうございます! あなたはブレイクポイントをセットしました。行 `8` の数字もクリックしましょう。 +これでブレイクポイントがセットできました。行 `8` の数字もクリックしましょう。 このようになるはずです(青はあなたがクリックした場所です): -![](chrome-sources-breakpoint.png) +![](chrome-sources-breakpoint.svg) *ブレイクポイント* はデバッガが自動でJavaScriptの実行を停止するコードのポイントです。 コードが停止している間、現在の変数を検査したり、コンソールでコマンドを実行することができます。つまり、そこでデバッグができます。 -右のペインでは、常にブレイクポイントの一覧を見ることができます。色々なファイルで多くのブレイクポイントを持っているときに役に立ちます。それらは次のことを可能にします: - -- コード中のブレイクポイントに素早く移動します(右ペインでそれをクリックします) -- チェックを外すことで、一時的にブレイクポイントを無効にします -- 右クリックで削除を選択することで、ブレイクポイントを削除します。 -- ...など。 +右のペインでは、常にブレイクポイントの一覧を見ることができます。色々なファイルで多くのブレイクポイントを設定しているときに役に立ちます。それらは次のようなことができます: +- コード中のブレイクポイントに素早く移動する(右ペインで移動したいブレイクポイントをクリック) +- チェックを外すことで、一時的にブレイクポイントを無効にする +- 右クリックから削除を選択することで、ブレイクポイントを削除する +- ...など ```smart header="条件付きのブレイクポイント" 行番号の *右クリック* で *条件付きの* ブレイクポイントを作ることができます。与えられた式が真の場合にのみトリガします。 -これは特定の変数値、もしくは関数パラメータに対してのみ停止する必要がある場合に便利です。 +これは変数の特定の値や、特定の関数パラメータに対してのみ停止する必要がある場合に便利です。 ``` ## デバッガコマンド -このように、`debugger` コマンドを使うことでもコードを停止することができます: +次のように、`debugger` コマンドを使うことでもコードを停止することができます: ```js function hello(name) { @@ -86,28 +84,28 @@ function hello(name) { } ``` -これは、コードエディタで作業中、ブラウザに切り替え開発者ツールのスクリプトを参照してブレイクポイントをセットしたくない場合に便利です。 +これは、コードエディタで作業中、ブラウザに切り替えて開発者ツールを起動し、ブレイクポイントをセットするために開発者ツールでスクリプトを探すなどという手間をかけたくない場合にとても便利です。 + ## 一時停止して見回す -今回の例では、`hello()` はページ読み込み中に呼び出されるので、デバッガを起動する最も簡単な方法はページを再読込することです。なので、 `key:F5` (Windows, Linux)または `key:Cmd+R` (Mac) を押しましょう。 +今回の例では、`hello()` はページ読み込み中に呼び出されるので、デバッガを起動する最も簡単な方法はページを再読み込みすることです。なので、 `key:F5` (Windows, Linux)または `key:Cmd+R` (Mac) を押しましょう。 ブレイクポイントがセットされているので、実行は4行目で一時停止します。: -![](chrome-sources-debugger-pause.png) +![](chrome-sources-debugger-pause.svg) -情報のドロップダウンを右に開いてください(矢印のラベルがついています)。現在のコードの状態を調べることができます: +右側にある情報のドロップダウンを開いてください(矢印のラベルがついています)。現在のコードの状態を調べることができます: 1. **`Watch` -- 任意の式の現在の値を表示します。** - `+` をクリックし、式を入力することができます。デバッガは、いつでもその値を表示し、実行中に自動的に再計算を行います。 + `+` をクリックし、式を入力することができます。デバッガは、常にその値を表示し、実行中に自動的に再計算を行います。 2. **`Call Stack` -- ネストされた呼び出しのチェーンを表示します。** 現時点では、デバッガは `hello()` 呼び出しの内側におり、`index.html` のスクリプト(そこに関数はないので、 "anonymous" と呼ばれます)によって呼び出されました。 スタックの項目をクリックすると、デバッガは該当のコードにジャンプし、すべての変数も同様に調べられます。 - 3. **`Scope` -- 現在の変数。** `Local` はローカル関数の変数を表示します。また、ソース上でもハイライト表示されたそれらの値を見ることができます。 @@ -118,38 +116,51 @@ function hello(name) { ## 実行を追跡する -今から、スクリプトを *追跡* する時間です。 - -右ペインの上部にそのボタンがあります。使ってみましょう。 +スクリプトを *追跡* してみましょう。 - -- 実行の継続, ホットキー `key:F8`. +右ペインの上部にそのボタンがあります。 + + -- "再開": 実行の継続, ホットキー `key:F8`. : 実行を再開します。もしも他にブレイクポイントがなければ、そのまま実行が継続され、デバッガの制御から外れます。 - 以下は、それをした後に見える画面です: + 次の図は、それを行った後に見える画面です: - ![](chrome-sources-debugger-trace-1.png) + ![](chrome-sources-debugger-trace-1.svg) 実行は再開され、`say()` の中の別のブレイクポイントに到達し、そこで一時停止します。右の "Call stack" を見てください。もう一度呼び出すことで増えています。私たちは、今 `say()` の中にいます。 - -- 一歩を踏み出します (次のコマンドを実行します)が、*関数の中には入りません*, ホットキー `key:F10`。 -: 今クリックすると `alert`が表示されます。 重要なのは、 `alert` はどの関数でも可能であり、実行は関数内部に入るのをスキップして実行するということです。 + -- "ステップ": 次のコマンドを実行します, ホットキー `key:F9`. +: 次の文を実行します。クリックすると、`alert` が表示されます。 + + これを何度もクリックすることで、1つずつスクリプト文が実行されます。 + + -- "ステップオーバー": 次のコマンドを実行しますが、*関数の中には入りません*, ホットキー `key:F10`。 +: 上の "ステップ" コマンドと同じですが、次の文が関数呼び出しの場合に振る舞いが異なります。つまり、`alert` のような組み込みではなく、我々自身が作成した関数です。 + + "ステップ" コマンドはその中に入り、その最初の行で実行を一時停止します。一方 "ステップオーバー" はネストされた関数呼び出しを目に見えない状態で実行し、関数の内部をスキップします。 + + その後、その関数の直後で実行が一時停止されます。 + + これは、関数呼び出しの内部で起きていることに興味がない場合に便利です。 + + -- "ステップイン", ホットキー `key:F11`. +: "ステップ" と同じですが、非同期関数呼び出しで振る舞いが異なります。もし JavaScript を学び始めたばかりであれば、まだ非同期呼び出しは取り扱ってないのでこの違いは無視してください。 - -- 一歩を踏み出します, ホットキー `key:F11`. -: 1つ前と同じですが、ネストされた関数に "ステップイン" します。。これをクリックすると、全てのスクリプトが1つずつ実行されます。 + 将来的には、"ステップ" コマンドは、後で実行される `setTimeout` (スケジューリングされた関数呼び出し) のような非同期アクションを無視することに注意してください。"ステップイン" はそれらのコードに入り、必要に応じてそれらを待ちます。詳細については[開発者マニュアル](https://developers.google.com/web/updates/2018/01/devtools#async) を参照してください。 - -- 現在の関数の最後まで実行を継続します、ホットキー `key:Shift+F11`。 -: 実行は現在の関数の最後の行で停止します。これは を使ってネストされた呼び出しに意図せず入ったが、興味はなくできるだけ早く終了したい場合に便利です。 + -- "ステップアウト": 現在の関数の最後まで実行を継続します、ホットキー `key:Shift+F11`。 +: 実行は現在の関数の最後の行で停止します。これは を使ってネストされた呼び出しに誤って入ってしまい、早く関数の終わりまで進めたい場合に便利です。 - -- すべてのブレイクポイントの有効/無効 -: そのボタンは実行を動かしません。単にブレイクポイントの on/off を切り替えます。 + -- すべてのブレイクポイントの有効/無効 +: このボタンは単にブレイクポイントの on/off を切り替えるもので、実行の状態は変わりません。 - -- エラー発生時の自動一時停止の有効/無効 -: 有効にして開発者ツールを開いている場合、スクリプトエラーが起きると実行が自動で一時停止します。そして、何が間違っていたかを知るために変数を分析することが出来ます。なので、スクリプトがエラーで死んだ場合は、どこで死んでその時どんなコンテキストであるかを見るために、デバッガを起動しこのオプションを有効にしてページを再読込しましょう。 + -- エラー発生時の自動一時停止の有効/無効 +: 有効にして開発者ツールを開いている場合、スクリプトエラーが起きると実行が自動で一時停止します。そして、何が間違っていたかを知るために変数を分析することができます。なので、スクリプトがエラーで死んだ場合は、どこで死んでその時どんなコンテキストであるかを確認するため、デバッガを起動しこのオプションを有効にしてページを再読込しましょう。 ```smart header="Continue to here" コードの行で右クリックすると、"Continue to here" と呼ばれる素晴らしい選択肢を持つコンテキストメニューが開きます。 -それは複数のステップを進めたいが、ブレイクポイントをセットするのが面倒なときに便利です。 +これは複数のステップを進めたいが、ブレイクポイントをセットするのが面倒なときに便利です。 ``` ## ロギング @@ -165,21 +176,21 @@ for (let i = 0; i < 5; i++) { } ``` -コンソールの中なので、通常のユーザはその出力を見ることはありません。見るためには、開発者ツールのコンソールタブを開くか、別のタブで `key:Esc` を押します。 :下にコンソールが表示されます。 +コンソールの中なので、通常のユーザはその出力を見ることはありません。見るためには、開発者ツールのコンソールタブを開くか、開発者ツールの別のタブで `key:Esc` を押します。 :下にコンソールが表示されます。 -コードに十分なログを仕込んでいれば、デバッガなしでその記録から何が行われているか知ることが出来ます。 +コードに十分なログを仕込んでいれば、デバッガなしで何が行われているか知ることができます。 ## サマリ -ご覧のとおり、スクリプトを一時停止するには主に3つの方法があります。 +これまで見てきた通り、スクリプトを一時停止するには主に3つの方法があります。 1. ブレイクポイント -2. `debugger` 構文 -3. エラー (開発者ツールを開き、ボタン を ON にしている場合) +2. `debugger` 文 +3. エラー (開発者ツールを開き、ボタン を ON にしている場合) これらにより変数を検査し実行が間違っている場所を確認することができます。 ここで説明した以上に、開発者ツールには多くのオプションがあります。完全なマニュアルは です。 -このチャプターの情報はデバッグを始めるのには十分ですが、今後、特にブラウザの作業が多い場合は、上記のサイトを見て開発者ツールのより高度な機能を調べてください。 +このチャプターの情報はデバッグを始めるには十分ですが、今後、特にブラウザの作業が多い場合は、上記のサイトを見て開発者ツールのより高度な機能を調べてください。 また、開発者ツールの色んな場所をクリックすることで何が表示されるかを見ることが出来ます。恐らくそれは開発者ツールを学ぶのに最も近道です。同様に右クリックも忘れないように! diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.png b/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.png deleted file mode 100644 index efa3c19df1..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.svg b/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.svg new file mode 100644 index 0000000000..a3c7db6ec4 --- /dev/null +++ b/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources.svg @@ -0,0 +1 @@ +open sources \ No newline at end of file diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources@2x.png deleted file mode 100644 index e184bdd01f..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-open-sources@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.png deleted file mode 100644 index 2fe449c9b6..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.svg b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.svg new file mode 100644 index 0000000000..6e7b60f854 --- /dev/null +++ b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint.svg @@ -0,0 +1 @@ +here's the listbreakpoints \ No newline at end of file diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint@2x.png deleted file mode 100644 index e4abc89d1f..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-breakpoint@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console.png deleted file mode 100644 index 98b22e777c..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console.svg b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console.svg new file mode 100644 index 0000000000..d5d2a0b934 --- /dev/null +++ b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console@2x.png deleted file mode 100644 index 3269a80f06..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-console@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause.png deleted file mode 100644 index 719293d2e5..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause.svg b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause.svg new file mode 100644 index 0000000000..83468fddb1 --- /dev/null +++ b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause.svg @@ -0,0 +1 @@ +213see the outer call detailswatch expressionscurrent variables \ No newline at end of file diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause@2x.png deleted file mode 100644 index 5c22ab361b..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-pause@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.png deleted file mode 100644 index 1848ccfacf..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.svg b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.svg new file mode 100644 index 0000000000..23937e0d68 --- /dev/null +++ b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1.svg @@ -0,0 +1 @@ +nested calls \ No newline at end of file diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1@2x.png deleted file mode 100644 index fcabf722eb..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-sources-debugger-trace-1@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-tabs.png b/1-js/03-code-quality/01-debugging-chrome/chrome-tabs.png deleted file mode 100644 index ff91c531f8..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-tabs.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-tabs.svg b/1-js/03-code-quality/01-debugging-chrome/chrome-tabs.svg new file mode 100644 index 0000000000..41a3d8784b --- /dev/null +++ b/1-js/03-code-quality/01-debugging-chrome/chrome-tabs.svg @@ -0,0 +1 @@ +213 \ No newline at end of file diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome-tabs@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome-tabs@2x.png deleted file mode 100644 index 09b10bf48d..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome-tabs@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome_break_error.png b/1-js/03-code-quality/01-debugging-chrome/chrome_break_error.png deleted file mode 100644 index 95399c7bb6..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome_break_error.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome_break_error@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome_break_error@2x.png deleted file mode 100644 index d9d576ecea..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome_break_error@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome_sources.png b/1-js/03-code-quality/01-debugging-chrome/chrome_sources.png deleted file mode 100644 index 0482bbed53..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome_sources.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome_sources@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome_sources@2x.png deleted file mode 100644 index fc65ed3fc5..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome_sources@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome_sources_break.png b/1-js/03-code-quality/01-debugging-chrome/chrome_sources_break.png deleted file mode 100644 index ac8fb1ff5d..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome_sources_break.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome_sources_break@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome_sources_break@2x.png deleted file mode 100644 index d6eadbe6f6..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome_sources_break@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome_sources_breakpoint.png b/1-js/03-code-quality/01-debugging-chrome/chrome_sources_breakpoint.png deleted file mode 100644 index 22fb9a5d57..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome_sources_breakpoint.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome_sources_breakpoint@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome_sources_breakpoint@2x.png deleted file mode 100644 index eba2b9bfc3..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome_sources_breakpoint@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome_sources_buttons.png b/1-js/03-code-quality/01-debugging-chrome/chrome_sources_buttons.png deleted file mode 100644 index 0f29946c3a..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome_sources_buttons.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/chrome_sources_buttons@2x.png b/1-js/03-code-quality/01-debugging-chrome/chrome_sources_buttons@2x.png deleted file mode 100644 index 7a16ea1c26..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/chrome_sources_buttons@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/console_error.png b/1-js/03-code-quality/01-debugging-chrome/console_error.png deleted file mode 100644 index ccf1b515a2..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/console_error.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/console_error@2x.png b/1-js/03-code-quality/01-debugging-chrome/console_error@2x.png deleted file mode 100644 index 4ab2fcea83..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/console_error@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/head.html b/1-js/03-code-quality/01-debugging-chrome/head.html index f219b0af18..615326c08e 100644 --- a/1-js/03-code-quality/01-debugging-chrome/head.html +++ b/1-js/03-code-quality/01-debugging-chrome/head.html @@ -1,8 +1,8 @@ diff --git a/1-js/03-code-quality/01-debugging-chrome/largeIcons.svg b/1-js/03-code-quality/01-debugging-chrome/largeIcons.svg new file mode 100644 index 0000000000..83303365bd --- /dev/null +++ b/1-js/03-code-quality/01-debugging-chrome/largeIcons.svg @@ -0,0 +1,1472 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + a + b + c + d + e + f + g + h + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + + + + + + + + + + + + + + + + + + + diff --git a/1-js/03-code-quality/01-debugging-chrome/manage1.png b/1-js/03-code-quality/01-debugging-chrome/manage1.png deleted file mode 100644 index f624a1fd83..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/manage1.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/manage1@2x.png b/1-js/03-code-quality/01-debugging-chrome/manage1@2x.png deleted file mode 100644 index 3f3c8116fc..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/manage1@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/manage2.png b/1-js/03-code-quality/01-debugging-chrome/manage2.png deleted file mode 100644 index a038e31069..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/manage2.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/manage2@2x.png b/1-js/03-code-quality/01-debugging-chrome/manage2@2x.png deleted file mode 100644 index 904280e499..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/manage2@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/manage3.png b/1-js/03-code-quality/01-debugging-chrome/manage3.png deleted file mode 100644 index 94bd7b31db..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/manage3.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/manage3@2x.png b/1-js/03-code-quality/01-debugging-chrome/manage3@2x.png deleted file mode 100644 index 3a988aa2ac..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/manage3@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/manage4.png b/1-js/03-code-quality/01-debugging-chrome/manage4.png deleted file mode 100644 index 04f57c72dc..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/manage4.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/manage4@2x.png b/1-js/03-code-quality/01-debugging-chrome/manage4@2x.png deleted file mode 100644 index d8758709ab..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/manage4@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/manage5.png b/1-js/03-code-quality/01-debugging-chrome/manage5.png deleted file mode 100644 index 55355c37db..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/manage5.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/manage5@2x.png b/1-js/03-code-quality/01-debugging-chrome/manage5@2x.png deleted file mode 100644 index c08f8a6868..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/manage5@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/manage6.png b/1-js/03-code-quality/01-debugging-chrome/manage6.png deleted file mode 100644 index cdd9bd0ef3..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/manage6.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/manage6@2x.png b/1-js/03-code-quality/01-debugging-chrome/manage6@2x.png deleted file mode 100644 index 52dad975a7..0000000000 Binary files a/1-js/03-code-quality/01-debugging-chrome/manage6@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/01-debugging-chrome/toolbarButtonGlyphs.svg b/1-js/03-code-quality/01-debugging-chrome/toolbarButtonGlyphs.svg deleted file mode 100644 index 5bdf20a83a..0000000000 --- a/1-js/03-code-quality/01-debugging-chrome/toolbarButtonGlyphs.svg +++ /dev/null @@ -1,1035 +0,0 @@ - -image/svg+xml \ No newline at end of file diff --git a/1-js/03-code-quality/02-coding-style/article.md b/1-js/03-code-quality/02-coding-style/article.md index 19e62ba1a3..60e3fd1480 100644 --- a/1-js/03-code-quality/02-coding-style/article.md +++ b/1-js/03-code-quality/02-coding-style/article.md @@ -1,18 +1,14 @@ # コーディングスタイル -私たちのコードは綺麗でできるだけ読みやすくなければなりません。 +コードはできるだけ綺麗で読みやすいものでなければなりません。 -それは実質プログラミングの芸術です -- 複雑なタスクを行い、正しく、人が読むことができる方法でコード化することです。 - -それを助ける一つが良いコードスタイルです。 - -[cut] +複雑なタスクを正しくかつ読みやすい形でコード化する、それはまさにプログラミングの極意です。優れたコーディングスタイルは、そのための大きな助けとなるのです。 ## 構文 -ルールに基づいたチートシート(より詳細は下): +下記は、いくつかの推奨ルールを示したチートシートです(詳細は後述): -![](code-style.png) +![](code-style.svg) -では、それらのルールと理由について詳細を説明します。 +では、これらのルールと理由について詳細を説明します。 -"もはや変えられない" ものはここにはありません。すべてはオプションであり、変更することが出来ます: それらはコーディングルールであり、宗教的な教義ではありません。 +```warn header="\"しなければならない\" というルールはありません。" +変えられないものはありません。これらはスタイルの好みであり、宗教的な教義ではありません。 +``` ### 波括弧 -ほとんどのJavaScriptのプロジェクトでは、波括弧は新しい行ではなく、同じ行に書かれます。いわゆる "エジプト" スタイルです。またそれは開始の括弧の前にスペースがあります。 +ほとんどのJavaScriptのプロジェクトでは、波括弧は新しい行ではなく、同じ行に書かれます。いわゆる "エジプト" スタイルです。また開始の括弧の前にはスペースがあります。 このようになります: @@ -56,35 +54,59 @@ if (condition) { } ``` -1行の構造も重要なエッジケースです。我々は括弧を使うべきですか?もしそうならどこででしょうか? +`if (condition) doSomething()` のような1行の構造も重要なエッジケースです。すべて括弧を使うべきでしょうか? -注釈付きのバリアントは次の通りです。あなた自身でその可読性を判断してみてください: +次にいくつか注釈付きでパターンを示します。あなた自身でその可読性を判断してみてください: - -![](figure-bracket-style.png) - -まとめると: -- 本当に短いコードは、`if (cond) return null` のように1行が許容されます:。 -- しかし、複数の文がある場合には、括弧内の各文ごとに行を分けるのが通常は良いです。 -### 行の長さ +また、`if` 文の場合は: -行の最大長は制限されるべきです。誰も長い水平な行は好きではありません。行を分ける方が良いです。 +```js +if ( + id === 123 && + moonPhase === 'Waning Gibbous' && + zodiacSign === 'Libra' +) { + letTheSorceryBegin(); +} +``` -行の最大長はチームで決められます。通常は 80 もしくは 120 文字です。 +1行の最大長は、チームレベルで合意しておくべきでしょう。通常は 80 または 120 文字です。 ### インデント @@ -92,8 +114,9 @@ if (n < 0) { - **水平なインデント: 2(4)個のスペース** - 水平なインデントは 2 または 4 つのスペース、もしくは "タブ" 記号を使います。どれを選ぶかは古い聖戦です。最近はスペースが一般的です。 - タブよりもスペースがよい利点の1つは、スペースは "タブ" 記号よりもより柔軟なインデントの設定ができることです。 + 水平なインデントは 2 または 4 つのスペース、もしくは "タブ" 記号(キー `key:Tab`)を使います。どれを選ぶかは好みの問題です。最近はスペースが一般的です。 + + タブよりもスペースの方がよい点の1つは、スペースは "タブ" 記号よりもより柔軟なインデントの設定ができることです。 例えば、このように、開始の括弧に対して引数を並べることができます: @@ -110,7 +133,7 @@ if (n < 0) { - **垂直のインデント: コードを論理ブロックに分割するための空行** - 1つの関数でさえ、頻繁に論理ブロックに分割されます。下の例では、変数の初期化、メインのループと結果返却は垂直に分かれています。: + 1つの関数であっても、多くの場合、論理的なブロックに分割可能です。下の例では、変数の初期化、メインのループ、結果の返却を縦方向に分割しています。: ```js function pow(x, n) { @@ -128,19 +151,19 @@ if (n < 0) { ### セミコロン -セミコロンは各文の後に存在するべきです。たとえ省略できるとしても。 +セミコロンは、たとえ省略できるとしても各文の末尾に存在するべきです。 -セミコロンが本当にオプションである言語がありますが、それはほとんど使用されていません。また、JavaScriptでは改行がセミコロンとして解釈されないケースがあり、プログラミングエラーの可能性を残します。 +セミコロンが本当にオプションで、ほとんど使われない言語もあります。また、JavaScriptでは改行がセミコロンとして解釈されないケースがあり、プログラミングエラーの可能性を残します。詳細については、チャプターを参照してください。 -プログラマとしてより成熟するにつれて、セミコロンなしのスタイルを選ぶかもしれません、[StandardJS](https://standardjs.com/), しかし、それはあなたがJavaScriptをよく知っており、かつ落とし穴の可能性を理解している場合にのみです。 +JavaScript のプログラマとしてより成熟するにつれて、[StandardJS](https://standardjs.com/) のようにセミコロンなしのスタイルを選ぶかもしれません。それ以外の場合、起こりうる落とし穴を避けるためセミコロンを使用するのが最善です。開発者の大多数はセミコロンをつけています。 ### ネストレベル -ネストのレベルが多くなり過ぎてはいけません。 +コードはネストし過ぎないようにしてください。 -`if(..) { ... }` で余分なネストを避けるために、ループで["continue"](info:while-for#continue)ディレクティブを使うことは、時には良いアイデアです。: +例えば、ループで余計なネストを避けるために ["continue"](info:while-for#continue)ディレクティブを使うことは、時には良いアイデアです。 -以下の代わりに: +また、このようにネストした `if` を追加する代わりに: ```js for (let i = 0; i < 10; i++) { @@ -150,7 +173,7 @@ for (let i = 0; i < 10; i++) { } ``` -こう書けます: +このように書けます: ```js for (let i = 0; i < 10; i++) { @@ -200,13 +223,13 @@ function pow(x, n) { } ``` -...`n < 0` というエッジケースは早い段階で処理されるため、余分なネストなしのメインのコードフローとなります。そのため、2つ目はより読みやすいです。 +2つ目のコードは、`n < 0` という特殊なケースが早い段階で処理されるため、より読みやすくなっています。このチェックが終わると、追加のネストを必要とせずにメインのコードフローに移ることができているからです。 -## コードの下の関数 +## 関数の配置 -もしいくつかの "ヘルパー関数"と、それを使うコードを書く場合、それらを配置する3つの方法があります。 +もしいくつかの "ヘルパー関数" を作り、それを使うコードを書く場合、それらを配置する方法が3つあります。 -1. それを使うコードの上に関数を記述する: +1. 関数を使用するコードより前に関数を記述する: ```js // *!*関数宣言*/!* @@ -227,7 +250,7 @@ function pow(x, n) { setHandler(elem); walkAround(); ``` -2. コードが最初で、その後に関数を記述 +2. 関数を使用するコードが最初で、その後に関数を記述 ```js // *!*関数を使用するコード*/!* @@ -251,17 +274,17 @@ function pow(x, n) { ``` 3. ミックス: 初めて使われる場所で関数を記述する -ほとんどの場合、2つ目がより好まれます。 +たいていの場合、2つ目がより好まれます。 -なぜならコードを読むとき、私たちは最初に "何をするか" を知りたいからです。コードが最初にくるとその情報が与えられます。そしてそれらの関数名が行うべきことに相応しいものであれば、関数の中身を読む必要は全くないかもしれません。 +なぜなら、コードを読むとき、私たちは最初に "何をするか" を知りたいからです。関数を使用するコードが先に書かれていれば、何をするかが最初から明確になります。そして、実際に何を行うかを関数名が表していれば、関数の中身を読む必要はまったくないかもしれません。 ## スタイルガイド スタイルガイドは "書き方" についての一般的なルールを含みます: どの引用符を使うか、インデントするスペースの数、改行を置く場所など、多くの細かいことがあります。 -全体でチーム全員が同じスタイルガイドを使うとき、コードは画一的になります。チームの誰がそれを書いても、同じスタイルになります。 +チームのメンバー全員が同じスタイルガイドを使用すると、どのメンバーが書いたかに関わらず、コードの見た目が統一されます。 -チームは自身のスタイルガイドを考えるかもしれません。しかし今のところそれをする必要はありません。多くの試行、鍛え上げられたスタイルガイドがすでにあり、それを適用するのは簡単です。 +もちろん、チームは自分たちのスタイルガイドを作ることができます。 ただしほとんどの場合、必要ありません。既に多くの実証済みの選択肢があるので、これらのうちの1つを採用するのが通常は最善の策です。 例えば: @@ -269,23 +292,21 @@ function pow(x, n) { - [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) - [Idiomatic.JS](https://github.com/rwaldron/idiomatic.js) - [StandardJS](https://standardjs.com/) -- (他にもあります) +- (他にもたくさんあります) -あなたが新米の開発者であれば、上のチートシートから始める事ができます。そしてその後、共通の原則を拾うためにスタイルガイドを参照し1つを選ぶかもしれません。 +あなたが新米の開発者であれば、この章の始めにあるチートシートから始めるとよいでしょう。その後、他のスタイルガイドを参照し、一般的な原則を知った上で最も好きなものを選択するのが良いでしょう。 ## 自動 linter -コードのスタイルを自動でチェックできるツールがあります。それらを "linter" と呼びます。 +Linter はコードのスタイルを自動でチェックし改善が提案できるツールです。 -それらの素晴らしい点は、スタイルチェックは変数や関数名の中のタイポなど、いくつかのバグも見つけることです。 +それらの素晴らしい点は、スタイルチェックは変数や関数名の中のタイポなど、いくつかのバグも見つけることです。なので、たとえ "コードスタイル" に固執したくない場合でも、それを導入することを推奨します。 -なので、たとえ "コードスタイル" に固執したくない場合でも、それを導入することを推奨します。それらはタイポを見つけるのに役立ち -- それだけで既に十分です。 +もっとも知られているツールはこれらです: -もっとも知られているツールは: - -- [JSLint](http://www.jslint.com/) -- one of the first linters. -- [JSHint](http://www.jshint.com/) -- more settings than JSLint. -- [ESLint](http://eslint.org/) -- probably the newest one. +- [JSLint](http://www.jslint.com/) -- 最初の linter の1つ +- [JSHint](http://www.jshint.com/) -- JSLint よりも多くの設定が可能 +- [ESLint](http://eslint.org/) -- 恐らく最も新しい linter これらどれでも利用できます。著者は [ESLint](http://eslint.org/) を使ってます。 @@ -293,9 +314,10 @@ function pow(x, n) { 例えば、ESLint では次のようなことをします。: -1. [Node.JS](https://nodejs.org/) をインストールします。 -2. `npm install -g eslint` コマンドで ESLint をインストールします(npm は Node.JS パッケージインストーラです) -3. あなたのJavaScriptプロジェクト(あなたのすべてのファイルを含むフォルダ)のルートに `.ellintrc` という名前の設定ファイルを作ります +1. [Node.js](https://nodejs.org/) をインストールします。 +2. `npm install -g eslint` コマンドで ESLint をインストールします(npm は Node.js パッケージインストーラです) +3. JavaScriptプロジェクト(すべてのファイルを含むフォルダ)のルートに `.eslintrc` という名前の設定ファイルを作ります +4. ESlint と統合するエディタのプラグインをインストール/有効化します。エディタの大多数はそれを持っています。 `.eslintrc` の例です: @@ -309,8 +331,8 @@ function pow(x, n) { }, "rules": { "no-console": 0, - }, - "indent": 2 + "indent": 2 + } } ``` @@ -320,7 +342,7 @@ function pow(x, n) { 代わりに、Webからスタイルのルールセットをダウンロードし、それを拡張することもできます。インストールについての詳細は、 を見てください。 -linter を使うと素晴らしい副作用があります: linter はタイポを見つけます。例えば、未宣言変数へのアクセスがあった場合、linter はそれを検出し、(もしもエディタと統合してれば)それをハイライトします。ほとんどのケースでそれはミスタイプです。よってすぐに直すことができます。 +上でも言いましたが、linter を使うと素晴らしい副次効果があります: linter はタイポを見つけます。例えば、未宣言変数へのアクセスがあった場合、linter はそれを検出し、(もしもエディタと統合してれば)それをハイライトします。ほとんどのケースでそれはタイプミスです。よってすぐに直すことができます。 そのような理由から、たとえスタイルについて関心がなくても、linter を利用することを強く勧めます。 diff --git a/1-js/03-code-quality/02-coding-style/code-style.png b/1-js/03-code-quality/02-coding-style/code-style.png deleted file mode 100644 index 278fd294cf..0000000000 Binary files a/1-js/03-code-quality/02-coding-style/code-style.png and /dev/null differ diff --git a/1-js/03-code-quality/02-coding-style/code-style.svg b/1-js/03-code-quality/02-coding-style/code-style.svg new file mode 100644 index 0000000000..12a755c975 --- /dev/null +++ b/1-js/03-code-quality/02-coding-style/code-style.svg @@ -0,0 +1 @@ +2No space between the function name and parentheses between the parentheses and the parameterIndentation 2 spacesA space after for/if/while…} else { without a line breakSpaces around a nested callAn empty line between logical blocksLines are not very longA semicolon ; is mandatorySpaces around operatorsCurly brace { on the same line, after a spaceA space between argumentsA space between parameters \ No newline at end of file diff --git a/1-js/03-code-quality/02-coding-style/code-style@2x.png b/1-js/03-code-quality/02-coding-style/code-style@2x.png deleted file mode 100644 index b576143455..0000000000 Binary files a/1-js/03-code-quality/02-coding-style/code-style@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/02-coding-style/figure-bracket-style.png b/1-js/03-code-quality/02-coding-style/figure-bracket-style.png deleted file mode 100644 index 112c2803ed..0000000000 Binary files a/1-js/03-code-quality/02-coding-style/figure-bracket-style.png and /dev/null differ diff --git a/1-js/03-code-quality/02-coding-style/figure-bracket-style@2x.png b/1-js/03-code-quality/02-coding-style/figure-bracket-style@2x.png deleted file mode 100644 index ce6e75c4dd..0000000000 Binary files a/1-js/03-code-quality/02-coding-style/figure-bracket-style@2x.png and /dev/null differ diff --git a/1-js/03-code-quality/03-comments/article.md b/1-js/03-code-quality/03-comments/article.md index 9e4e84bb0e..19764f6f58 100644 --- a/1-js/03-code-quality/03-comments/article.md +++ b/1-js/03-code-quality/03-comments/article.md @@ -1,10 +1,10 @@ # コメント -チャプター でご存知の通り、コメントは1行(`//` で始まります)または複数行(`/* ... */`)にできます。 + の章で見たように、コメントは `//` で始まる1行、もしくは `/* ... */` 形式の複数行とすることができます。 -通常はそれを使用して、どうやって/なぜそのコードが動作するのかを説明します。 +通常は、どのように、そしてなぜコードが動作するのかを説明するためにコメントを使用します。 -一見するとコメントすることは当たり前かもしれませんが、プログラム初心者は通常間違った思い込みをします。 +一見、コメント付けというのはやるべきことが明白なようですが、プログラミング初心者は間違ったコメントの使い方をすることがあるようです。 ## 悪いコメント @@ -18,13 +18,13 @@ complex; code; ``` -しかし良いコードでは、このような "説明的な" コメントは最小限にすべきです。真面目にコードはそれらなしで理解しやすくするべきです。 +しかし良いコードでは、このような "説明的な" コメントは最小限にすべきです。真面目にそれらがなくても理解しやすいコードにするべきです。 それに関して素晴らしいルールがあります。"もしもコードがコメントを必要とするほど不明瞭な場合、書き直すべきかもしれません"。 -### レシピ: 関数を取り除く +### レシピ: 機能を括り出す -時々、このようにコードの一部を関数に置き換えることは有益です: +このように、コードの一部を関数に置き換えることは有益な場合があります: ```js function showPrimes(n) { @@ -65,11 +65,11 @@ function isPrime(n) { } ``` -今や、私たちは簡単にコードを理解することが出来ます。関数自身がコメントになります。このようなコードは *自己記述的* と呼ばれます。 +今や、私たちは簡単にコードを理解することができます。関数自身がコメントになります。このようなコードは *自己記述的* と呼ばれます。 ### レシピ: 関数を作成する -また、もしこのような長い "コードシート" を持っている場合: +また、もしこのような長い "コードの塊" がある場合: ```js // here we add whiskey @@ -90,7 +90,7 @@ for(let t = 0; t < 3; t++) { // ... ``` -次のような関数にリファクタリングするのがより良い方法かもしれません: +次のように関数にリファクタリングするのがより良い方法かもしれません: ```js addWhiskey(glass); @@ -111,60 +111,60 @@ function addJuice(container) { } ``` -改めて言いますが、関数自身が何が行われているのかを伝え、コメントすることは何もありません。また分割するとコードの構造はより良くなります。各関数がすること、何を取り何を返すのかは明白です。 +改めて言いますが、関数自身が何が行われているのかを伝えているので、コメントすることは何もありません。また分割するとコードの構造はより良くなります。各関数がすること、何を引数として取り、何を返すのかは明白です。 -現実では、完全に "説明的な" コメントを避けることはできません。複雑なアルゴリズムがあります。そして最適化のためのスマートな "微調整" があります。しかし、一般的にはコードをシンプルで自己記述的に保つよう努めるべきです。 +現実では、完全に "説明的な" コメントを避けることはできません。複雑なアルゴリズムがあり、その最適化のための賢明な "微調整" がコードの中で行われることがあります。しかし、一般的にはコードをシンプルで自己記述的に保つよう努めるべきです。 ## 良いコメント -これまでの通り、説明的なコメントは通常良くありません。ではどんなコメントが良いでしょう? +これまでの通り、説明的なコメントは通常良くありません。ではどんなコメントが良いのでしょう? アーキテクチャの説明をする -: 高水準のコンポーネントの概要、相互作用の方法、様々な状況での制御フローを説明します... つまり -- コードの俯瞰図です。それは高水準のアーキテクチャ図のための特別な言語[UML](http://wikipedia.org/wiki/Unified_Modeling_Language)があります。これは間違いなく学ぶ価値があります。 +: 高水準のコンポーネントの概要、相互作用の方法、様々な状況での制御フローを説明します... つまり -- コードの俯瞰図です。それは高水準のアーキテクチャ図のための特別な言語[UML](https://ja.wikipedia.org/wiki/%E7%B5%B1%E4%B8%80%E3%83%A2%E3%83%87%E3%83%AA%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E)があります。これは間違いなく学ぶ価値があります。 関数の使用方法を文書化する -: 関数の文書化のための特別な構文 [JSDoc](http://en.wikipedia.org/wiki/JSDoc) があります。: 使用方法、パラメータ、返却値 - - 例: - ```js - /** - * Returns x raised to the n-th power. - * - * @param {number} x The number to raise. - * @param {number} n The power, must be a natural number. - * @return {number} x raised to the n-th power. - */ - function pow(x, n) { - ... - } - ``` +: 関数の文書化のための特別な構文 [JSDoc](https://ja.wikipedia.org/wiki/JSDoc) があります。: 使用方法、パラメータ、返却値 + +例: +```js +/** + * Returns x raised to the n-th power. + * + * @param {number} x The number to raise. + * @param {number} n The power, must be a natural number. + * @return {number} x raised to the n-th power. + */ +function pow(x, n) { + ... +} +``` - このようなコメントにより、関数の目的を理解し、コードの中を見ることなく正しい方法で利用することができます。 +このようなコメントにより、関数の目的を理解し、コードの中を見ることなく正しい方法で利用することができます。 - ところで、[WebStorm](https://www.jetbrains.com/webstorm/) のような多くのエディタも同様にそれらを解釈することができ、オートコンプリートやいくつかの自動コードチェックを提供するのに使います。 +ちなみに、[WebStorm](https://www.jetbrains.com/ja-jp/webstorm/) のような多くのエディタも同様にそれらを解釈することができ、それらを使ってオートコンプリートや自動コードチェックを提供します。 - また、コメントからHTMLドキュメントを生成することができる [JSDoc 3](https://github.com/jsdoc3/jsdoc) のようなツールもあります。JSDocに関するより多くの情報は で読むことができます。 +また、コメントからHTMLドキュメントを生成することができる [JSDoc 3](https://github.com/jsdoc3/jsdoc) のようなツールもあります。JSDocに関するより多くの情報は で読むことができます。 -なぜこのように解決されるのか? -: 何が書かれているかは重要です。が、起こっていることを理解するためには、*書かれていないこと* がより重要かもしれません。正確にはなぜそのタスクがこの方法で解決されるのか?コードは回答しません。 +タスクがこのように解決されるのはなぜか? +: 書かれていることは重要です。が、何が起こっていることを理解するためには、*書かれていないこと* がより重要かもしれません。なぜそのタスクがこの方法で正しく解決されるのか?コードは回答しません。 - もしそのタスクを解決る方法が沢山有る場合、なぜこれを選んだのでしょう?特に、それが最も明白なものではないとき。 + もしそのタスクを解決する方法が多数ある場合、なぜこれを選んだのでしょう?特に、それが最も明白なものではないとき。 このようなコメントがなければ、次のような状況が起こりえます: 1. あなた(もしくは同僚) はいくらか前に書かれたコードを開き、それが "準最適" であることを確認します。 - 2. あなたは考えます: "私はなんて愚かだったのか、そして今はどれだけスマートになったのか"、そして "より明白で正しい" 方法を使って書き直します。 - 3. ... 書き直すと言う衝動は良かったです。しかし、そのプロセスではあなたは "より明白な" 解決策は実際には不十分であることに気付きます。既にずっと前にそれを試みており、なぜダメなのかをぼんやり覚えています。あなたは最終的に正しい方法に戻しますが、時間が無駄になりました。 + 2. あなたは考えます: "私はなんて愚かだったのか、そして今はどれだけ賢くなったのか"、そして "より明白で正しい" 方法を使って書き直します。 + 3. ... 書き直したいという思いは良かったです。しかし、そのプロセスの中であなたは "より明白な" 解決策は実際には不十分であることに気付きます。実は既に以前試みたことであり、なぜダメだったのかぼんやり覚えています。最終的に元の正しい方法に戻します。が、その分時間が無駄になりました。 - 解決策を説明するコメントはとても重要です。それらは正しい方向で開発を続けるのを助けます。 + 解決策を説明するコメントはとても重要です。それらは正しい方向で開発を続けるのに役立ちます。 コードの捉えにくい特徴はある?それらはどこで使われる? -: もしもコードが捉えにくく、紛らわしいものがある場合には、きっとコメントする価値があります。 +: もしもコードが捉えにくく、紛らわしいものがある場合にはきっとコメントする価値があります。 ## サマリ -よい開発者の重要なサインはコメントです: それらの存在、またそれらの欠如。 +よい開発者であることを示す重要な指標は、コメントです: 何を書くか、また、何を書かないかさえ、指標となります。 -よいコメントはコードを上手く維持し、時間が経った後にそこに戻り、より有効に使用できるようにします。 +よいコメントはコードを上手く維持し、時間が経った後でそこに戻ったときにも効果的に使えるようにします。 **コメントすること:** @@ -174,7 +174,7 @@ function addJuice(container) { **コメントを避ける:** -- "どのようにコードが動くか" そして "それが何をするか" を伝える -- コードを、コメントを必要としないような、シンプルで自己記述的にすることが不可能な場合にのみ置きます。 +- コメントは、 "どのようにコードが動くか" そして "それが何をするか" を伝えるものです。 +- コメントを必要としないほどシンプルで自明なコードにすることが不可能な場合にのみ、コメントを入れます。 -コメントはJSDoc3のような自動文書化ツールにも使われます: それらはコメントを読んで、HTML-docs(または別の形式の文書)を生成します。 +コメントはJSDoc3のような自動文書化ツールにも使われます: それらはコメントを解釈し、HTML-docs(または別の形式の文書)を生成します。 diff --git a/1-js/03-code-quality/04-ninja-code/article.md b/1-js/03-code-quality/04-ninja-code/article.md index 285fb00675..540cac32ee 100644 --- a/1-js/03-code-quality/04-ninja-code/article.md +++ b/1-js/03-code-quality/04-ninja-code/article.md @@ -1,50 +1,59 @@ # 忍者コード -過去のプログラマの忍者は、コード管理者泣かせのトリックを使いました。コードレビューの専門家はテストタスクでそれらを探します。新米の開発者はプログラマ忍者よりも優れたものを利用する場合があります。 -これらを注意深く読んで、あなたが誰であるかを知ってください -- 忍者、初心者、またはコードレビューア? +```quote author="孔子" +子曰く、学びて思わざれば則ち罔し(くらし)、思いて学ばざれば則ち殆し(あやうし)。 +``` + +過去のプログラマー忍者は、コード管理者泣かせのトリックを使いました。 + +コードレビューの達人は、テストタスクでそれらを探します。 + +新米の開発者は、プログラマー忍者よりもそれらをうまく使うことがあります。 + +これらを注意深く読んで、あなたが誰であるかを知ってください -- 忍者、初心者、またはコードレビュアーでしょうか? -[cut] ```warn header="諷刺" -ここに書いてあるのは悪いコードを書き込むルールです。なので、一部の人々には的を得ていません。 +多くの人が忍者の道を辿ろうとしますが、成功することはほとんどありません。 ``` -## 簡潔は機知の精髄 -できるだけ短くコードを書きましょう。どれだけあなたがスマートか示しましょう。 +## 簡潔が肝心(Brevity is the soul of wit) -捉えにくい言語機能があなたを導きます。 +できるだけコードを短くしましょう。あなたがどれだけ賢いかを示しましょう。 -例えば、この3項演算子 `'?'` を見てください: +そのためには、すぐには捉えにくい言語機能も使いましょう。 + +例えば、この3項演算子 `'?'` を見てください: ```js // よく知られている javascript ライブラリから持ってきました i = i ? i < 0 ? Math.max(0, len + i) : i : 0; ``` -クールですよね?もしあなたがそのように書いたら、この行に来て `i` の値が何かを理解しようとする開発者は、愉快な時間を過ごすことになります。そして、あなたのところに来て、答えを求めます。 +すごいですよね?もしあなたがこのように書いたら、この行を見て `i` の値が何かを理解しようとする開発者は、愉快な時間を過ごすことになります。そして、あなたのところに来て、答えを求めるでしょう。 より短いことが常により良いと教えましょう。彼を忍者の道に導きましょう。 -## 一文字の変数 +## 一文字の変数 + +```quote author="老子道徳経" +道は隠れたもので、名も無い。それ唯、道は善くゆとりを与え、且つ、成すものだ。 -```quote author="Laozi (Tao Te Ching)" -道は言葉にならないことの中に隠れています。道だけが上手く始まり、よく完成しました。 -(The Dao hides in wordlessness. Only the Dao is well begun and well completed.) ``` -より速くコード化(そしてはるかに悪くなる!)する別の方法は、いたるところで1文字の変数名を使うことです。`a`, `b` や `c` のように。 +より速くコード化するためのもう1つの方法は、あらゆる場所で、`a`, `b` や `c` のように1文字の変数名を使うことです。 -森の中での本物の忍者のように、短い変数はコードの中で消えます。だれもエディタの "検索" を使って見つけることが出来ません。そして、たとえ誰かがそうしたとしても、`a` や `b` が意味することを "解読" することができないでしょう。 +森の中の忍者のように、短い変数はコードの中で消えます。エディタの "検索" を使って見つけることはできません。たとえできたとしても、`a` や `b` が意味することを "解読" することはできないでしょう。 -...しかし、例外があります。本当の忍者は決して `"for"` ループのカウンタとして `i` を使いません。どこにでもありますが、ここではありません。周囲を見回して、多くのよりエキゾチックな文字があります。例えば `x` または `y` です。 +...しかし、例外があります。本物の忍者は決して `"for"` ループのカウンタに `i` を使いません。色々な所で使いますが、ここでは使いません。見回すと、より多くのエキゾチックな文字があります。例えば `x` または `y` です。 -ループカウンタとしてのエキゾチックな変数は、ループ本体が 1-2 ページ(できればより長くする)の場合は特にクールです。そのループを深く見ている人には、変数名 `x` がループカウンタであることはすぐにはわからないでしょう。 +ループ本体が 1-2 ページ(できればより長くする)の場合は、ループカウンタとしてのエキゾチックな変数は特に効果があります。そのループを深く見ている人は、変数名 `x` がループカウンタであることはすぐには分からないでしょう。 -## 略語を使用する +## 略語を使用する -もしもチームのルールが1文字で漠然とした名前の使用を禁止している場合、それらを短縮して略語を作ってください。 +チームのルールで、1文字や曖昧な名前の使用を禁止している場合、それらを短縮して略語を作ってください。 このように: @@ -53,36 +62,38 @@ i = i ? i < 0 ? Math.max(0, len + i) : i : 0; - `browser` -> `brsr`. - ...etc -本当に良い直感を持っている人だけが、そのような名前を理解することが出来ます。すべてを短縮してみましょう。あなたのコードを支えることができるのは、有能な人だけです。 +本当に感が鋭い人だけがこのような名前を理解することができます。すべてを短くするようにしてください。あなたのコードを支持できるのは、有能な人だけです。 -## 高く舞い上がる、抽象的になる +## 高く舞い上がる、抽象的になる -```quote author="Laozi (Tao Te Ching)" -The great square is cornerless
-The great vessel is last complete,
-The great note is rarified sound,
-The great image has no form. +```quote author="老子道徳経" +大方は隅(ぐう)無し。
+大器は晩成(ばんせい)す。
+大音聲希(まれ)なり。
+大象(たいしょう)は形無し。 ``` -名前を選んでいるとき、最も抽象的な言葉を使ってみてください。`obj`, `data`, `value`, `item`, `elem` など。 +名前を選ぶときは、最も抽象的な言葉を使ってください。`obj`, `data`, `value`, `item`, `elem` など。 -- **変数の理想の名前は `data`** どこでもそれを使いましょう。確かにすべての変数は *データ* を保持していますよね。 +- **変数の理想の名前は `data`** どこでもこれを使いましょう。確かに、すべての変数は *データ* を保持していますよね。 - ...しかし、もし `data` が既に取られていたらどうしますか? `value` を試みてみましょう、それもまた普遍的です。結局、変数は最終的に *値* を取得します。 + ...しかし、もし `data` が既に使われていたらどうしましょう? その時は `value` を試みてみましょう、それもまた普遍的です。結局、変数は最終的に *値* を得るので。 - **変数をその型で命名する: `str`, `num`...** - 試してみましょう。若い忍者は不思議に思うかもしれません -- このような名前はコードを悪化させるのか?実はそうです! + 試してみましょう。若い忍者は不思議に思うかもしれません -- このような名前は本当に忍者のためになるのでしょうか?はい、その通りです。 - 一方で、そうは言っても変数名は何かを意味しています。それは変数の中身のことです: 文字列、数値またはそれ以外の何か。しかし外部からコードを理解しようとするとき、実際には全く情報がないことに驚くでしょう! + 確かに、変数名は中身に何があるかを意味しています: 文字列、数値またはそれ以外の何か。しかし外部からこのコードを理解しようとするとき、実際にはまったく情報がないことに驚くでしょう。 - 実際、値の型はデバッグで簡単にわかります。しかし変数が意味するものが何か?どの文字列/数値が格納されるのか?良い瞑想なしでそれを理解する方法はありません。 + 値の型はデバッグで簡単にわかります。しかし変数が意味するものが何か?どの文字列/数値が格納されるのか? + + 相当の熟慮なしでそれを理解する方法はありません。 -- **...しかし、それ以上にこのような名前がなかったらどうしますか?** 単に数値を付け足します: `data1, item2, elem5`... +- **...しかし、これ以上このような名前がなかったら?** 単に数値を付け足します: `data1, item2, elem5`... -## 注意力テスト +## 注意力テスト -本当に気が利くプログラマだけがそのコードを理解できます。しかし、どうやってそれをチェックしましょう? +本当に気が利くプログラマだけがあなたのコードを理解できます。しかし、どうやってそれをチェックしましょう? **1つの方法は -- 似た変数名を使うことです。`date` や `data` のように** @@ -90,36 +101,37 @@ The great image has no form. このようなコードを素早く読むことは不可能です。また、タイポがある場合...うーん...私たちは長い時間行き詰まります。 -## スマートな同義語 -```quote author="Confucius" +## スマートな同義語 + +```quote author="孔子" 最も難しいことは暗い部屋で黒猫を見つけることです。そこに猫がいない場合は特にそうです。 ``` -*同じ* もののために *類似の* 名前を使うことは人生をより面白くし、あなたの創造性を外に示します。 +*同じ* ものに対して *類似の* 名前を使うことは人生をより面白くし、あなたの創造性を外部に示します。 -例えば、関数のプレフィックスを考えてください。もし関数が画面上にメッセージ表示するとしたら、 -- `displayMessage` のように `display…` から始めます。そして、次に別の関数でユーザ名のような、何かを画面に表示するとき、`showName` など `show…` から始めます。 +例えば、関数のプレフィックスを考えてください。もし関数が画面上にメッセージ表示する場合、 -- `displayMessage` のように `display…` から始めます。そして、次に別の関数でユーザ名のような、何かを画面に表示するとき、`showName` など `show…` から始めます。 -実際にはそこには何も意図はありませんが、このような関数の間で微妙な違いをほのめかしましょう。 +実際には何もありませんが、このような関数の間で微妙な違いをほのめかしましょう。 -チームの仲間の忍者と契約を結びましょう: もしジョンが "表示をする" 関数を `display...` で始めたら、ピーターは `render...` を使い、アンは -- `paint...` を。どのくらい面白くて多様なコードになったかに注意してください。 +チームの仲間の忍者と契約を結びましょう: もしジョンが "表示をする" 関数を `display...` で始めたら、ピーターは `render...` を使い、アンは -- `paint...` を。どのくらい面白くて多様なコードになったかに注目してください。 ...そして今やハットトリックです! 重要な違いを持つ2つの関数では -- 同じプレフィックスを使いましょう! -例えば、関数 `printPage(page)` はプリンタを使うでしょう。そして関数 `printText(text)` は画面上にテキストを表示します。使い慣れていない読み手に類似の名前がつけられた関数 `printMessage` について考えてさせましょう。: "これはどこにメッセージを置きますか?プリンタ、または画面?" 本当に輝かせるためには `printMessage(message)` は新しいウィンドウに表示するべきです! +例えば、関数 `printPage(page)` はプリンタを使うでしょう。そして関数 `printText(text)` は画面上にテキストを表示します。使い慣れていない読み手に類似の名前がつけられた関数 `printMessage` について考えさせましょう: "この関数はどこにメッセージを出すの?プリンタ?それとも画面上?" 本当に輝かせるためには `printMessage(message)` は新しいウィンドウに表示するべきです! -## 名前の再利用 +## 名前の再利用 -```quote author="Laozi (Tao Te Ching)" -Once the whole is divided, the parts
-need names.
-There are already enough names.
-One must know when to stop. +```quote author="老子道徳経" +始めて制して名有り。
+名も亦既に有るも、
+それ亦止まることを知らんとす。
+止まることを知るは、殆(あやう)からざる所以なり。 ``` -間違いなく必要なときにだけ、新しい変数を追加してください。 +本当に必要なときにだけ、新しい変数を追加してください。 代わりに、既存の名前を再利用してください。新しい値をそこに書き込みます。 @@ -127,7 +139,7 @@ One must know when to stop. そうすれば、変数 *now* に入っているものを正確に特定するのは本当に難しくなります。また、それがどこから来るのかも。直感の弱い人は1行ずつコードを解析し、すべてのコードのブランチの変更を追跡する必要があります。 -**そのアプローチの高度のバリアントは、ループや関数の途中で、同質の何かにこっそり (!) 置き換えることです。** +**そのアプローチの高度のパターンは、値をループや関数の途中で、こっそり (!) 似たものに置き換えることです。** 例えば: @@ -137,7 +149,7 @@ function ninjaFunction(elem) { elem = clone(elem); - // さらに 20 行, 今や elem のクローンを処理しています! + // さらに 20 行, いつの間にか elem のクローンを処理しています! } ``` @@ -149,24 +161,25 @@ function ninjaFunction(elem) { 変数名の前にアンダースコア `_` と `__` を置きましょう、`_name` もしくは `__value` のように。もしあなただけがその意味を知っているなら素晴らしいです。もしくは、特に意味はなく単に楽しむために追加するのもよいです。異なる場所で異なる意味をもたせるのも良いです。 -あなたは、一度のショットで、2匹のうさぎを殺します。1つ目は、コードがより長くなり可読性を下げます。2つ目は、仲間の開発者はアンダースコアの意味を知ろうとするために長い時間を費やすかもしれません。 +あなたは、一発の弾丸で2匹のうさぎを殺します。1つ目は、コードがより長くなり可読性を下げます。2つ目は、仲間の開発者はアンダースコアの意味を知ろうとするために長い時間を費やすかもしれません。 -賢い忍者はコードの1ヶ所にアンダースコアを置き、別の場所では避けます。これにより、さらにコードが壊れやすくなり、未来のエラーの可能性が高まります。 +賢い忍者はコードの1ヶ所にアンダースコアを置き、別の場所では避けます。これにより、さらにコードが壊れやすくなり、将来エラーが起きる可能性が高まります。 -## あなたの愛を見せる +## あなたの愛を示す みんなにあなたの存在がどれだけ壮大かを見せてください! `superElement`, `megaFrame` や `niceItem` のような名前はきっと読者を啓発します。 -確かに、片方では何かが書かれています: `super..`, `mega..`, `nice..`。 しかしその一方では -- それは詳細をもたらしません。読者はその隠された意味を探し、1〜2時間そのために瞑想するかもしれません。 +確かに、変数名には `super..`, `mega..`, `nice..` などが書かれています。が、その一方で -- それらはその詳細を何も示しません。読者はその隠された意味を探すために時間を割くかもしれません。 -## 外部の変数と重ね合わせる -```quote author="Guan Yin Zi" -When in the light, can't see anything in the darkness.
-When in the darkness, can see everything in the light. +## 外部の変数と重ね合わせる + +```quote author="関尹子" +明るい処に身を置く者は、暗闇の中に一つの物を見ることもできないが。
+暗闇に身を置く者は、明るい所の細々とした事を区別することができる。 ``` -関数の内側と外側で同じ変数名を使ってください。単純で努力は不要です。 +関数の内側と外側で同じ変数名を使ってください。簡単で努力は不要です。 ```js let *!*user*/!* = authenticateUser(); @@ -183,28 +196,29 @@ function render() { `render` の内側へジャンプしてきたプログラマは、恐らくローカルの `user` が外の `user` を隠していることに気づかないでしょう。 -そして、外部変数、`authenticateUser()` の結果であるという想定で `user` を使って処理しようと試みるでしょう... トラップが飛び出しました!こんにちは、デバッガー... +そして、外部変数、`authenticateUser()` の結果であるという想定で `user` を使って処理しようと試みるでしょう... トラップが飛び出しました! デバッガーの出番です... + -## 至るところで副作用! +## 至るところで副作用! -何も変えないように見える関数があります。 `isReady()`, `checkPermission()`, `findTags()`... それらは、外側のものを内も変えることなく、データを計算したり、見つけたり返したりすると想定されています。言い換えると、"副作用" なしです。 +何も変えないように見える関数があります。 `isReady()`, `checkPermission()`, `findTags()`... これらの関数は、外側のものを何も変えることなく、データを計算したり、見つけて返したりすると想定されています。つまり、"副作用" なしです。 -**本当に美しいトリックはそれらに "役立つ" アクションを追加することです、メインの処理に加えて。** +**本当に美しいトリックは、メインの処理に加えて "役立つ" アクションを追加することです。** -`is..`, `check`, または `find..` と名づけられた関数が何かを変更するとき、あなたの同僚の顔の驚きの表情は、きっとあなたの境界を広げるでしょう。 +`is..`, `check`, または `find..` と名づけられた関数が何かを変更すると、同僚の驚きの表情は、きっとあなたの理性のたがを広げるでしょう。 -**驚かせるための別の方法は標準ではない結果を返すことです。** +**驚かせるためのもう1つの方法は、標準ではない結果を返すことです。** -あなたのオリジナルの考えを見せましょう! `checkPermission` の呼び出しで、 `true/false` ではなく、確認結果の複雑なオブジェクトを返すようにしましょう。 +あなたのオリジナルの考えを見せましょう! `checkPermission` の呼び出しで、 `true/false` ではなく、結果の確認が複雑なオブジェクトを返すようにしましょう。 `if (checkPermission(..))` を書こうとした開発者はなぜ動かないのか不思議に思うでしょう。彼らに教えましょう: "ドキュメントを読みなさい!" そしてこの記事を見せてください。 -## 強力な関数! +## 強力な関数! -```quote author="Laozi (Tao Te Ching)" -The great Tao flows everywhere,
-both to the left and to the right. +```quote author="老子道徳経" +大道は汎として、
+それ左右すべし。 ``` その名前に書かれていることで関数を制限しないでください。広くあれ。 @@ -213,14 +227,14 @@ both to the left and to the right. 追加のアクションは関数名から明白であってはなりません。本当の忍者のコーダは、同様にコードからもそれを明らかにしません。 -**いくつかのアクションを1つに結合すると、あなたのコードを再利用から守ります。** +**複数のアクションを1つに結合すると、あなたのコードを再利用から守ります。** -想像してみてください、emailのチェックだけ行い、メッセージを出力したくない開発者を。両方を行うあなたの関数 `validateEmail(email)` は彼にはマッチしません。そのため、彼はそれについて何かを尋ねるようなことはしないので、あなたの瞑想が中断させられることはありません。 +想像してみてください。emailのチェックだけ行い、メッセージを出力したくない開発者を。両方を行うあなたの関数 `validateEmail(email)` は彼にはマッチしません。そのため、彼はそれについて何かを尋ねるようなことはしないので、あなたの作業が中断させられることはありません。 ## サマリ -すべての上の "アドバイス" は実際のコードからです... ときどき、経験豊富な開発者により書かれています。たぶんあなたよりも経験豊かな ;) +これまでの "アドバイス" はすべての実際のコードからです... 経験豊富な開発者により書かれていることもあります。たぶんあなたよりも経験豊かな ;) - それらのいくつかに従うと、あなたのコードは驚きに満ちるでしょう。 -- それらの多くをフォローすると、あなたのコードはあなたのものになり、誰もそれを変更したくないでしょう。 -- すべてを守れば、あなたのコードは、啓発を求める若い開発者にとって貴重な教訓になるでしょう。 +- それらの多くに従うと、あなたのコードはあなただけのものになり、誰もそれを変更したくないでしょう。 +- すべてを守れば、あなたのコードは啓発を求める若い開発者にとって貴重な教訓になるでしょう。 diff --git a/1-js/03-code-quality/05-testing-mocha/article.md b/1-js/03-code-quality/05-testing-mocha/article.md index f1e285bdf7..9d4becca00 100644 --- a/1-js/03-code-quality/05-testing-mocha/article.md +++ b/1-js/03-code-quality/05-testing-mocha/article.md @@ -1,46 +1,42 @@ # mocha による自動テスト -さらなるタスクでは、自動化テストが使用されます。 +多くのタスクがある場合、自動テストが使用され、実際のプロジェクトでも広く利用されています。 -実際、それは開発者の "最低限の教育" の一部です。 +## なぜテストが必要なのでしょうか? -[cut] +関数を書くとき、どのパラメータがどのような結果をもたらすかといった、関数がすべきことをイメージできます。 -## なぜテストが必要? +開発中、私たちは関数を実行し、その結果を期待される値と比較することで関数のチェックが行えます。例えば、コンソール上でそれを行うことができます。 -関数を書くとき、通常それがすることをイメージできます: どのパラメータがどの結果となるか。 - -開発中、私たちは関数を実行し、その結果と期待値を比較することで確認を行うことができます。例えば、コンソール上でそれを行うことができます。 - -もし何かが間違っていたら -- コードを直し、再度実行し結果を確認します -- と言ったことを上手く動くまで。 +もし何かが間違っていたら -- コードを直し、再度実行し結果を確認 -- を期待通り動くまで繰り返します。 しかしそのような手動の "再実行" は十分ではありません。 -**手動の再実行でコードをテストする時、間違えやすいです** +**手動による再実行でコードをテストする場合、何かを簡単に見逃してしまうことがあります。** -例えば、関数 `f` を作っています。いくらかコードを書き、テストします: `f(1)` は動きますが、`f(2)` は動きません。コードを直すと `f(2)` が動くようになります。これで完璧に見えますか?しかし `f(1)` の再テストを忘れており、エラーになるかもしれません。 +例えば、関数 `f` を作っています。ある程度コードを書き、テストします: `f(1)` は動きますが、`f(2)` は動きません。コードを直すと `f(2)` が動くようになります。が、これで完璧でしょうか? `f(1)` の再テストを忘れており、このケースがエラーになるかもしれません。 -これは非常に典型的なパターンです。何かを開発するとき、多くのユースケースの可能性を心に留めます。しかし、全ての変更のたびに手動ですべてをチェックするのをプログラマに期待するのは難しいです。なので、1つを直し別の1つを壊すことは容易に起こります。 +これは非常に典型的なパターンです。何かを開発するとき、私たちは多くのユースケースの可能性を心に留めています。しかし、何かの変更のたびに、すべてのユースケースのチェックを手動で行うことをプログラマに期待するのは困難です。このため、あるものを修正して別のものを壊すことが容易に起こり得るのです。 -**自動テストは、コードに加えてテストが別々に書かれていることを意味します。それらは簡単に実行でき、全ての主要なユースケースをチェックすることができます。** +**自動テストとは、コードとは別にテストを記述することを意味します。これにより、関数はさまざまな方法で実行され、期待される結果との比較が行われます。** ## ビヘイビア駆動開発(BDD) -[ビヘイビア駆動開発](http://en.wikipedia.org/wiki/Behavior-driven_development),もしくは BDD と呼ばれるテクニックを使ってみましょう。そのアプローチは多くのプロジェクトの間で使われています。BDDは単なるテストについてのものではありません。それ以上です。 +[ビヘイビア駆動開発](http://en.wikipedia.org/wiki/Behavior-driven_development),もしくは BDD と呼ばれるテクニックを使ってみましょう。そのアプローチは多くのプロジェクトで使われています。なお、BDDは単なるテストについてのものではありません。それ以上です。 -**BDD には3つのことがあります。テスト、ドキュメンテーション、そして例です。** +**BDD には3つのことがあります。テスト、ドキュメント、そして例です。** -十分な言葉です。例を見てみましょう。 +BDD を理解するために、実際の開発ケースを検討していきましょう。 -## "pow" の開発: 仕様 +## "pow" の開発: spec -`x` の `n` 乗をする関数 `pow(x, n)` が作りたいとしましょう。`n≥0` と仮定します。 +`x` の `n` 乗をする関数 `pow(x, n)` を作りたいとしましょう。`n≥0` と仮定します。 -このタスクは単に例です: JavaScriptではそれをするための `**` 演算子がありますが、ここでは、より複雑なタスクに対しても同様に適応することができる開発フローに集中します。 +このタスクは例です: JavaScriptには同様のことを行う `**` 演算子がありますが、ここではより複雑なタスクに対しても同様に適用が可能な *開発フロー* に集中します。 -私たちは、`pow` のコードを作成する前に、関数が何をすべきかを想像することができます。 +`pow` のコードを作成する前に、関数が何をすべきかを考え、それを記述します。 -このような記述は *仕様*、もしくはスペックと呼ばれ、このようになります: +このようなことに関する記述は *specification(仕様)*、もしくは spec と呼ばれ、次のようになります: ```js describe("pow", function() { @@ -52,48 +48,50 @@ describe("pow", function() { }); ``` -上記の通り、仕様は三つの主要な構成要素を持っています: +上記の通り、spec は三つの主要な構成要素を持っています: `describe("title", function() { ... })` -: 何の機能を記述しているか。"ワーカー" -- `it` のブロックのことです、をグループ化するために使います。このケースでは、関数 `pow` について記述しています。 +: どのような機能について記述しているか。"ワーカー" -- `it` のブロック -- をグループ化するために使用します。このケースでは、関数 `pow` について記述しています。 `it("title", function() { ... })` -: `it` のタイトルでは、特定のユースケースを人間が読めるように記述し、2つ目の引数は、それをテストするための関数です。 +: `it` の `title` では、特定のユースケースを人間が読めるように記述し、2番目の引数は、それをテストするための関数です。 `assert.equal(value1, value2)` -: `it` ブロックの中のコード、もし実装が正しければエラーなく実行されます。 +: もし実装が正しければ、`it` ブロック内のコードはエラーなく実行されるはずです。 - 関数 `assert.*` は`pow`が期待通り動作するかをチェックするために使われます。ここでは、その1つを使っています -- `assert.equal`, それは引数を比較し、等しくない場合にエラーを返します。ここでは `pow(2, 3)` の結果が `8` と等しいかをチェックします。 + 関数 `assert.*` は`pow`が期待通り動作するかをチェックするために使われます。ここではそのうちの1つ、`assert.equal` を使用しています。これは引数を比較し、等しくない場合にエラーを返します。ここでは `pow(2, 3)` の結果が `8` と等しいかをチェックします。他のタイプの比較やチェックもありますので、後ほど紹介します。 - 他のタイプの比較やチェックもたくさんあります。 +この仕様は実行可能であり、実行されると `it` ブロック内で指定されたテストが実施されます。これについては後ほど見ていきます。 ## 開発フロー 開発フローは通常このようになります: -1. 最も基本的な機能のテストと一緒に、初期仕様が書かれます。 -2. 初期の実装がされます。 -3. それが動くかを確認するため、テストフレームワークである [Mocha](http://mochajs.org/) (より詳細はこの後) を実行します。エラーが表示されます。すべてが動作するまで修正を行います。 -4. テストと一緒に、動作する初期の実装ができあがります。 -5. まだ実装でサポートされていない可能性のある、より多くのユースケースを仕様(テスト)に追加します。テストは失敗し始めます。 +1. 最も基本的な機能のテストとともに、初期の spec が書かれます。 +2. 最初の実装が作成されます。 +3. 動作するかどうかを確認するため、テストフレームワークである [Mocha](http://mochajs.org/) (詳細は後述) を使用して spec を実行します。機能が完全ではない場合、エラーが表示されます。すべてが動作するまで修正を行います。 +4. これで、テスト付きの動作可能な初期の実装ができあがります。 +5. まだ実装ではサポートされていないであろうユースケースを、さらに spec に追加していきます。これによりテストは失敗し始めます。 6. 3に戻り、テストのエラーが無くなるまで実装を更新します。 -7. 機能が準備できる準備できるまで3-6のステップを繰り返します。 +7. 機能が完成するまで3〜6のステップを繰り返します。 -従って、開発は *反復* です。仕様を書き、それを実装し、テストが通ることを確認し、さらにテストを書いて、それらが動作することを確認します。最終的に、動作する実装とテストが出来上がります。 +上で分かる通り、開発は *反復* です。spec を書き、それを実装し、テストが通ることを確認し、さらにテストを書いて、それらが動作することを確認します。最終的に、動作する実装とテストができ上がります。 -私たちのケースでは、最初のステップは完了です: `pow` の初期仕様を持っています。なので実装しましょう。しかし、その前にテストが動作すること(それ自体はすべて失敗します)を試してみましょう。 +実践的なケースでこの開発フローをみていきましょう。 -## アクションの仕様 +最初のステップはすでに完了です: `pow` の初期 spec を持っています。では、実装を行う前に、テストを実行するためのいくつかの JavaScript ライブラリを利用してそれらが動作することを見ててみましょう(テスト自体は失敗します)。 + +## アクションの spec このチュートリアルでは、テストのために次のJavaScriptライブラリを使います: -- [Mocha](http://mochajs.org/) -- コアフレームワーク: `describe` や `it` を含む共通のテスト関数と、テストを実行するメインの機能を提供します。 -- [Chai](http://chaijs.com) -- 多くのアサーションを持つライブラリ。様々なアサーションを使う事ができます。今は `assert.equal` だけ必要です。 -- [Sinon](http://sinonjs.org/) -- 関数をスパイするためのライブラリで、組み込み関数とそれ以上のものをエミュレートします。私たちは、後でこれらを必要とします。 +- [Mocha](http://mochajs.org/) -- コアフレームワーク: `describe` や `it` を含む共通のテスト関数と、テストを実行するメイン機能を提供します。 +- [Chai](http://chaijs.com) -- 多くのアサーションを持つライブラリ。様々なアサーションを使う事ができますが、今は `assert.equal` だけが必要です。 +- [Sinon](http://sinonjs.org/) -- 関数をスパイしたり、組み込み関数をエミュレートしたりするライブラリで、後々必要となります。 -これらのライブラリはブラウザとサーバサイド両方のテストに適しています。ここでは、ブラウザのバリアントについて検討します。 +これらのライブラリはブラウザとサーバサイド両方のテストで利用することができます。ここでは、ブラウザの場合について進めます。 -それらのフレームワークと `pow` 仕様の完全なHTMLページ: +それらのフレームワークと `pow` の spec の完全なHTMLページです: ```html src="index.html" ``` @@ -101,7 +99,7 @@ describe("pow", function() { ページは4つのパートに分かれています: 1. `` -- テストのためのサードパーティーのライブラリやスタイルの追加 -2. テストするための関数の ` - ``` +// あるいは、ローカル変数に currentUser がある場合には、 +// 明示的に window から取得することも可能です +alert(window.currentUser.name); // John +``` - ここで、最初の2つの alert は現在のウィンドウを使い、後の2つは、`iframe` ウィンドウからの変数を取ります。`iframe` が同じプロトコル/ホスト/ポートから発信されている場合、任意の変数にすることができます。 +とはいえ、グローバル変数は一般的には推奨されません。できるだけ少なくするべきです。関数が "入力" 変数を得て、明確な "結果" を出力するというコードデザインは、外部やグローバル変数を使用する場合よりも明確でエラーも少なく、テストもしやすいです。 -## "this" とグローバルオブジェクト +## polyfill のための使用 -時々、`this` の値はまさにグローバルオブジェクトです。それはめったに使われませんが、いくつかのスクリプトはそれに依存しています。 +グローバルオブジェクトを使って、最近の言語機能のサポートをテストします。 -1. ブラウザにおいて、グローバル領域の `this` の値は `window` です: +例えば、組み込みの `Promise` オブジェクトが存在するかテストします(本当に古いブラウザでは存在しません): +```js run +if (!window.Promise) { + alert("Your browser is really old!"); +} +``` - ```js run - // 関数の外側 - alert( this === window ); // true - ``` +存在しない場合、"polyfill" を作成できます(その環境でサポートされていないが、最近の標準としては存在する機能を追加)。 - 他の非ブラウザ環境では、このようなケースでは `this` として別の値を使う可能性があります。 +```js run +if (!window.Promise) { + window.Promise = ... // 新しい言語機能のカスタム実装 +} +``` -2. 非 strict モードで `this` を使った関数が呼ばれた場合、`this` としてグローバルオブジェクトを取ります: +## サマリ - ```js run no-strict - // not in strict mode (!) - function f() { - alert(this); // [object Window] - } +- グローバルオブジェクトはどこでも参照可能な変数を保持しています。 - f(); // オブジェクトなしでの呼び出し - ``` + `Array` などJavaScript組み込みのものや、`window.innerHeight`(ブラウザのウィンドウの高さ)のような環境固有のものも含みます。 +- グローバルオブジェクトは普遍な名前 `globalThis` を持っています。 - 仕様によると、Node.JS のような非ブラウザも含め、このケースでの `this` はグローバルオブジェクトである必要があります。それは古いスクリプトのための互換性です。strict モードでは、`this` は `undefined` になります。 + ...ですが、多くの場合、`window`(ブラウザ)や`global`(Node.js)のような、昔ながらの環境固有の名前で呼ばれます。 +- グローバルオブジェクトに値を保存するのは、プロジェクトで本当にグローバルであるものだけにすべきです。そして、その数はできるだけ少なくすべきです。 +- ブラウザでは、[モジュール](info:modules)を使用していない限り、`var` で宣言されたグローバル関数や変数はグローバルオブジェクトのプロパティになります。 +- コードを将来性があり理解しやすくするために、グローバルオブジェクトのプロパティへは `window.x` のような直接アクセスしたほうがよいです。 diff --git a/1-js/06-advanced-functions/06-function-object/article.md b/1-js/06-advanced-functions/06-function-object/article.md index 45c9c5c430..e6141153eb 100644 --- a/1-js/06-advanced-functions/06-function-object/article.md +++ b/1-js/06-advanced-functions/06-function-object/article.md @@ -1,9 +1,9 @@ # 関数オブジェクト, NFE -既に知っている通り、JavaScriptの関数は値です。 +既にご存知の通り、JavaScriptの関数は値です。 -JavaScriptのすべての値は型を持っています。関数は何の型でしょうか? +JavaScriptのすべての値は型を持っています。関数は型は何でしょうか? JavaScriptでは、関数はオブジェクトです。 @@ -65,9 +65,7 @@ alert(user.sayHi.name); // sayHi alert(user.sayBye.name); // sayBye ``` -しかし、正しい名前を見つける方法がない場合があります。 - -その時、このように空になります。: +しかし、正しい名前を把握する方法がない場合があります。そのようなとき、name プロパティは次ように空になります。: ```js // 配列の中で作られた関数 @@ -181,7 +179,7 @@ alert( counter() ); // 1 `count` は今や外部のレキシカル環境ではなく、関数の中に直接格納されています。 -クロージャを使う要理も悪いのでしょうか?それとも良いのでしょうか? +クロージャを使うよりも悪いのでしょうか?それとも良いのでしょうか? 主な違いは、`count` の値が外部変数にある場合、外部コードはそこへアクセスすることはできないということです。ネストされた関数だけがそれを変更することができます。: @@ -326,8 +324,7 @@ sayHi = null; welcome(); // Hello, Guest (ネスト呼び出しは機能します) ``` -これでうまく動作します。なぜなら名前 `"func"` は関数ローカルだからです。それは外部のものではありません(また、外部からは見えません)。 -この仕様は常に現在の関数を参照することを保証します。 +名前 `"func"` は関数ローカルなのでこれでうまく動作します。それは外部のものではありません(外部からは見えません)。仕様は常に現在の関数を参照することを保証します。 外部コードは依然として変数 `sayHi` または後の `welcome` を持っています。そして、`func` は "内部の関数名" であり、自身を呼び出すためのものです。 @@ -352,4 +349,5 @@ welcome(); // Hello, Guest (ネスト呼び出しは機能します) それらは "メインの" 関数を作り、それに多くの "ヘルパー" 関数を付与します。例えば、[jquery](https://jquery.com) ライブラリは `$` という名前の関数を作ります。[lodash](https://lodash.com) ライブラリは関数 `_` を作ります。そして、 `_.clone`, `_.keyBy` や他のプロパティを追加します(これらについてもっと知りたい場合は、[docs](https://lodash.com/docs) を参照してください)。実際には、グローバル空間の汚染を少なくするために、1つのライブラリで1つのグローバル変数のみが与えられます。 これにより、名前の競合が発生する可能性が低くなります。 + したがって、関数は単独で有益な仕事をすることができ、プロパティには他の機能も備えることができます。 diff --git a/1-js/06-advanced-functions/07-new-function/article.md b/1-js/06-advanced-functions/07-new-function/article.md index e6fb347d19..12ca0014a9 100644 --- a/1-js/06-advanced-functions/07-new-function/article.md +++ b/1-js/06-advanced-functions/07-new-function/article.md @@ -1,9 +1,7 @@ # "new Function" 構文 -関数を作るもう1つの方法があります。ほとんど使われませんが、代替手段がない場合が時々あります。 - -[cut] +関数を作るもう1つの方法があります。ほとんど使われませんが、代替手段がないことがたまにあります。 ## 構文 @@ -13,11 +11,9 @@ let func = new Function ([arg1[, arg2[, ...argN]],] functionBody) ``` -言い換えると、関数パラメータが最初で、本体が最後に来ます。全ての引数は文字列です。 - -例を見ると理解し易いです。 +引数 `arg2...argN` と指定された `functionBody` で関数が作成されます。 -例えば、ここでは2つの引数を持つ関数です: +例を見ると理解し易いです。以下は2つの引数を持つ関数です: ```js run let sum = new Function('a', 'b', 'return a + b'); @@ -25,7 +21,7 @@ let sum = new Function('a', 'b', 'return a + b'); alert( sum(1, 2) ); // 3 ``` -もし引数がない場合、1つの引数(関数本体)だけになります。: +引数がない場合は関数本体だけを指定します: ```js run let sayHi = new Function('alert("Hello")'); @@ -33,9 +29,9 @@ let sayHi = new Function('alert("Hello")'); sayHi(); // Hello ``` -これまで見てきたような他の方法との大きな違いは -- 関数は文字列から文字通り作られ、実行時に渡されるということです。 +これまで見てきたような他の方法との大きな違いは、関数は文字通り文字列から作られ、実行時に渡されるということです。 -これまでの宣言では、プログラマーはスクリプトに関数コードを書く必要がありました。 +これまでのすべての宣言は、プログラマーがスクリプトに関数コードを書く必要がありました。 しかし、`new Function` は任意の文字列を関数にすることができます。例えば、サーバから新しい関数を受け取りそれを実行することができます: @@ -46,16 +42,17 @@ let func = new Function(str); func(); ``` -これはサーバからコードを受け取ったり、テンプレートから動的に関数をコンパイルするような、非常に特定のケースで使われます。その必要性は通常開発がかなり進んだ段階で発生します。 +これは 複雑なWebアプリケーションでサーバからコードを受け取ったり、テンプレートから動的に関数をコンパイルするような、非常に特定のケースで使われます。 ## クロージャ -通常、関数は特別なプロパティ `[[Environment]]` でどこで生成されたかを覚えています。それは作成された場所からレキシカル環境を参照します。 +通常、関数は特別なプロパティ `[[Environment]]` でどこで生成されたかを覚えています。それは作成された場所からレキシカル環境を参照します( の章で説明しました)。 しかし、`new Function` を使用して作られた関数の場合、その `[[Environment]]` は現在のレキシカル環境ではなく、グローバルのレキシカル環境を参照します。 -```js run +そのため、このような関数は外部変数へのアクセスは持たず、グローバル変数のみとなります。 +```js run function getFunc() { let value = "test"; @@ -87,45 +84,30 @@ getFunc()(); // *!*"test"*/!*, getFunc のレキシカル環境から この `new Function` の特殊な機能は奇妙に見えますが、実践では非常に役立ちます。 -本当に文字列から関数を作る必要がある場合をイメージしてください。その関数のコードはスクリプト生成時には知られていません(そういう訳で通常の関数を使うこができません)が、実行中に認識されます。我々はサーバや別のソースからそれを受け取ることができます。 - -新しい関数はメインスクリプトと相互作用する必用があります。 - -私たちは、それが外部のローカル変数へアクセスできるようにしたいかもしれません。 - -しかし、問題はJavaScriptが本番環境に公開される前に、*minifier* -- 余分なコメントやスペースなどを削除することでコード小さくする特別なプログラムで、より重要なことはローカル変数をより短いものにリネームします。 -- を使用して圧縮されていることです。 +本当に文字列から関数を作る必要がある場合をイメージしてください。その関数のコードはスクリプト生成時には知られていません(そういう訳で通常の関数を使うことができません)が、実行中に認識されます。我々はサーバや別のソースからそれを受け取ることができます。 -例えば、もし関数が `let userName` を持っていたとき、minifier はそれを `let a` (または既に使われていれば別の文字) に置き換え、随所でそれを実行します。変数はローカルであり関数の外部からアクセスすることはできないため、それは通常安全です。また、関数の内側では minifier はそれに関する全ての箇所を置き換えます。Minifiers は賢いので、単なる検索と置換ではなく、コード構造を分析するので問題ありません。 +新しい関数はメインのスクリプトと相互にやり取りが必要です。 -...しかしもし `new Function` が外部変数へアクセスできる場合、`userName` を見つけることはできません。 +外部変数へアクセスしたいときはどうするのでしょうか? -**たとえ `new Function` で外部のレキシカル環境へアクセスできたとしても、minifiers で問題になります。** +問題はJavaScriptが本番環境に公開される前に、*minifier*(余分なコメントやスペースなどを削除することでコード小さくする特別なプログラム)を使用して圧縮されており、重要なことはローカル変数をより短いものにリネームします。 -`new Function` の "特別な機能" は私たちをミスから救います。 +例えば、もし関数が `let userName` を持っていたとき、minifier はそれを `let a` (または既に使われていれば別の文字) に置き換え、その変数を使用します。変数はローカルであり関数の外部からアクセスすることはできないため、それは通常安全です。また、関数の内側では minifier はそれに関する全ての箇所を置き換えます。Minifiers は賢いので、単なる検索と置換ではなく、コード構造を分析するので問題ありません。 -そして、より良いコードを実行します。 `new Function`によって作成された関数に何かを渡す必要がある場合、引数として明示的に渡す必要があります。 +そのため、`new Function` が外部変数へアクセスする場合、リネームされた `userName` を見つけることはできません。 -"sum" 関数は実際このようになります: +**`new Function` が外部変数へアクセスする場合、minifiers で問題になります。** -```js run -*!* -let sum = new Function('a', 'b', ' return a + b; '); -*/!* - -let a = 1, b = 2; +加えて、このようなコードはアーキテクチャ的に良くなく、エラーになりやすいです。 -*!* -// 引数として外部変数が渡される -alert( sum(a, b) ); // 3 -*/!* -``` +`new Function` として作成された関数に何かを渡すには、その引数を使用しなければなりません。 ## サマリ 構文: ```js -let func = new Function(arg1, arg2, ..., body); +let func = new Function ([arg1, arg2, ...argN], functionBody); ``` 歴史的な理由から、引数はカンマ区切りのリストで与えられます。 @@ -138,4 +120,4 @@ new Function('a,b', ' return a + b; '); // カンマ区切り new Function('a , b', ' return a + b; '); // スペースありのカンマ区切り ``` -`new Function` で作られた関数は グローバルレキシカル環境を参照する `[[Environment]]` を持っており、外部のレキシカル環境ではありません。従って、それらは外部の変数を使うことができません。しかし、それは実際に良いことです。なぜなら、それは我々をエラーから守るからです。明示的なパラメータ渡しは構造的にははるかに優れており、minifierには問題ありません。 +`new Function` で作られた関数は グローバルレキシカル環境を参照する `[[Environment]]` を持っており、外部のレキシカル環境ではありません。従って、それらは外部の変数を使うことができません。ですが、それは良いことです。明示的なパラメータ渡しはアーキテクチャ的にはよりよい方法であり、minifierの問題も起きません。 diff --git a/1-js/06-advanced-functions/08-settimeout-setinterval/3-rewrite-settimeout/solution.md b/1-js/06-advanced-functions/08-settimeout-setinterval/3-rewrite-settimeout/solution.md index 42a6a00468..0183a47997 100644 --- a/1-js/06-advanced-functions/08-settimeout-setinterval/3-rewrite-settimeout/solution.md +++ b/1-js/06-advanced-functions/08-settimeout-setinterval/3-rewrite-settimeout/solution.md @@ -15,7 +15,7 @@ function count() { if (i == 1000000000) { alert("Done in " + (Date.now() - start) + 'ms'); - cancelInterval(timer); + clearInterval(timer); } } diff --git a/1-js/06-advanced-functions/08-settimeout-setinterval/article.md b/1-js/06-advanced-functions/08-settimeout-setinterval/article.md index 17641bea0b..044df46577 100644 --- a/1-js/06-advanced-functions/08-settimeout-setinterval/article.md +++ b/1-js/06-advanced-functions/08-settimeout-setinterval/article.md @@ -7,9 +7,7 @@ - `setTimeout` は指定時間経過後、一度だけ関数を実行します。 - `setInterval` は各実行の間は指定した間隔で、定期的に関数を実行します。 -それらのメソッドは JavaScript の仕様の一部ではありません。しかしほとんどの環境は内部スケジューラをもち、それらのメソッドを提供します。特に、これらはすべてのブラウザと Node.JS でサポートされています。 - -[cut] +それらのメソッドは JavaScript の仕様の一部ではありません。しかしほとんどの環境は内部スケジューラをもち、それらのメソッドを提供します。特に、これらはすべてのブラウザと Node.js でサポートされています。 ## setTimeout @@ -100,7 +98,7 @@ clearTimeout(timerId); alert(timerId); // 同じ 識別子 (キャンセル後 null にはなりません) ``` -`alert` の出力から分かるように、ブラウザではタイマー識別子は数値です。他の環境では、それは他の何かの場合があります。例えば、Node.JS だと、追加メソッドを持つタイマーオブジェクトを返します。 +`alert` の出力から分かるように、ブラウザではタイマー識別子は数値です。他の環境では、それは他の何かの場合があります。例えば、Node.js だと、追加メソッドを持つタイマーオブジェクトを返します。 改めて、それらのメソッドのための普遍的な仕様はありませんので問題ありません。 @@ -128,17 +126,17 @@ let timerId = setInterval(() => alert('tick'), 2000); setTimeout(() => { clearInterval(timerId); alert('stop'); }, 5000); ``` -```smart header="Chrome/Opera/Safari ではモーダルウィンドウは時間を止めます" -IEとFirefoxでは、内部タイマーは `alert/confirm/prompt` を表示している間も "作動" し続けますが、Chrome、Opera、Safariでは内部タイマーは "凍結" します。 +```smart header="`alert`が表示されている間、時間は経過します" +Chrome や Firefox を含むほとんどのブラウザは、`alert/confirm/prompt` を表示している間、内部のタイマーが継続されます。 -従って、もし上のコードを実行し、しばらく `alert` ウィンドウを消さなかった場合、Firefox/IE では次の `alert` はウィンドウを消した直後に表示されます(前の実行から2秒経過しているため)。Chrome/Opera/Safari では -- 2秒後に表示されます(タイマーは `alert` 中は作業していなかったため)。 +そのため、上のコードを実行した後、しばらく `alert` ウィンドウを消さなかった場合、次の `alert` はすぐに表示されます。実際のインターバル間隔は2秒よりも短くなります。 ``` -## 再帰的な setTimeout +## 再帰的な(ネストされた) setTimeout -何かを定期的に実行するのに 2つの方法があります。 +定期的に何かを実行するのに 2つの方法があります。 -1つは、`setInterval` です。もう1つは、再帰的な `setTimeout` で、このようになります: +1つは、`setInterval` です。もう1つは、ネストされた `setTimeout` で、このようになります: ```js /** 次の代わり: @@ -186,7 +184,7 @@ let timerId = setTimeout(function request() { ```js let i = 1; setInterval(function() { - func(i); + func(i++); }, 100); ``` @@ -195,14 +193,14 @@ setInterval(function() { ```js let i = 1; setTimeout(function run() { - func(i); + func(i++); setTimeout(run, 100); }, 100); ``` -`setInterval` では、内部スケジューラは100ms秒毎に `func(i)` を実行します。: +`setInterval` では、内部スケジューラは100ms秒毎に `func(i++)` を実行します。: -![](setinterval-interval.png) +![](setinterval-interval.svg) 気づきましたか...? @@ -218,9 +216,9 @@ setTimeout(function run() { 次に、これは再帰的な `setTimeout` の図です: -![](settimeout-interval.png) +![](settimeout-interval.svg) -**再帰的な `setInterval` は固定の遅延 (ここでは 100ms) を保証します。** +**再帰的な `setTimeout` は固定の遅延 (ここでは 100ms) を保証します。** 新しい呼び出しは、以前の呼び出しの終わりに計画されるためです。 @@ -232,14 +230,14 @@ setTimeout(function run() { setTimeout(function() {...}, 100); ``` -`setInterval` では `cancelInterval` が呼ばれるまで、関数はメモリ上に存在し続けます。 +`setInterval` では `clearInterval` が呼ばれるまで、関数はメモリ上に存在し続けます。 そこには副作用があります。関数は外部のレキシカル環境を参照するので、それが生きている間は外部の変数も生き続けます。それらは関数自身よりもはるかに多くのメモリを必要とする場合があります。従って、スケジュールされた機能がもう必要ないときは、たとえそれが非常に小さいとしても、それをキャンセルする方がいいです。 ```` -## setTimeout(...,0) +## 遅延なしの setTimeout -特別なユースケースがあります: `setTimeout(func, 0)` です。 +特別なユースケースがあります: `setTimeout(func, 0)`、あるいは単に `setTimeout(func)` です。 これは `func` をできるだけ速く実行するようスケジュールします。しかし、スケジューラは現在のコードが完了した後にそれを実行します。 @@ -255,112 +253,9 @@ alert("Hello"); 最初の行は "0ms 後のカレンダーに呼び出しを置いています"。しかし、スケジューラは現在のコードが完了した後に "カレンダーのチェック" をします。そのため、 `"Hello"` が最初で、`"World"` が後になります。 -### CPUを必要とするタスクの分割 - -`setTimeout` を使ってCPUを必要とするタスクを分割するトリックがあります。 - -たとえば、構文強調表示スクリプト(このページのコード例を色分けするために使用されます)はかなりCPUが重いです。 コードを強調表示するために、分析を実行し、多くの色の要素を作成し、文書に追加します。 ブラウザが "ハングアップ" することさえあり、それは容認できません。 - -そこで、私たちは長いテキストを小さく分割することができます。`setTimeout(...,0)` を使って、最初の100行、次の100行を計画する、と言ったように。 - -わかりやすくするために、より単純な例を考えてみましょう。 `1` から `1000000000` まで数える関数があります。 - -それを実行する場合、CPUはハングアップするでしょう。サーバサイド JS だと容易に気付き、ブラウザで実行した場合にはページ上の他のボタンをクリックしようとしても -- 実際にはJavaScript全体が一時停止しています。 - -```js run -let i = 0; - -let start = Date.now(); - -function count() { - - // 重い処理を実行 - for (let j = 0; j < 1e9; j++) { - i++; - } - - alert("Done in " + (Date.now() - start) + 'ms'); -} - -count(); -``` - -ブラウザは "スクリプトが時間がかかりすぎている" 警告を出す場合があります。 - -入れ子の `setTimeout` を使ってジョブを分割しましょう: - -```js run -let i = 0; - -let start = Date.now(); - -function count() { - - // 重い処理の一部を実行 (*) - do { - i++; - } while (i % 1e6 != 0); - - if (i == 1e9) { - alert("Done in " + (Date.now() - start) + 'ms'); - } else { - setTimeout(count, 0); // 新しい呼び出しをスケジュール (**) - } - -} - -count(); -``` - -これで、ブラウザUIは "カウント" 処理中でも完全に機能します。 - - -`(*)` でジョブの一部を行います: - -1. 最初の実行: `i=1...1000000`. -2. 2回めの実行: `i=1000001..2000000`. -3. ...が続き、`while` は `i` が `100000` で均等に分割されているかどうかをチェックします。 - -そして、まだ終わっていない場合には `(**)` で次の呼び出しがスケジュールされます。 - -`count` の実行間の休止は、JavaScriptエンジンが何か他のことをしたり、他のユーザアクションに反応するのに十分な "一息" を提供します。 - -注目すべき点は、両方のバリアントです: `setTimeout` によりジョブを分割してもしなくてもスピードは同等です。全体のカウント時間に大きな違いはありません。 - -それらをもっと近づけるために改善しましょう。 - -`count()` の先頭にスケジューリングを移動させます: +また、 の章で説明する、遅延なしの timeout の高度なブラウザ関連のユースケースもあります。 -```js run -let i = 0; - -let start = Date.now(); - -function count() { - - // 開始時にスケジューリングを移動する - if (i < 1e9 - 1e6) { - setTimeout(count, 0); // 新しい呼び出しをスケジュール - } - - do { - i++; - } while (i % 1e6 != 0); - - if (i == 1e9) { - alert("Done in " + (Date.now() - start) + 'ms'); - } - -} - -count(); -``` - -これで、`count()` を開始して `count()` をもっと呼ぶ必要があると知ったとき -- 私たちはジョブを実行する前に、すぐにそれをスケジュールします。 - -それを実行すると、時間が大幅に短縮されることに簡単に気づきます。 - -````smart header="ブラウザにおけるネストされたタイマーの最小遅延" +````smart header="遅延ゼロは実際にはゼロではありません(ブラウザにおいて)" ブラウザでは、ネストされたタイマーを実行できる頻度に制限があります。[HTML5 標準](https://www.w3.org/TR/html5/webappapis.html#timers) では次のように書かれています: "5つのネストされたタイマーの後には...間隔は少なくとも4ミリ秒に強制されます。" 何を意味しているか、下の例でデモしてみましょう。例での `setTimeout` 呼び出しは、自身を `0ms` 後に実行するよう再スケジュールします。各呼び出しは `times` 配列に、直前のものからの実行時間を覚えています。実際の遅延はどのように見えるでしょう?見てみましょう: @@ -380,81 +275,24 @@ setTimeout(function run() { // 1,1,1,1,9,15,20,24,30,35,40,45,50,55,59,64,70,75,80,85,90,95,100 ``` -最初のタイマーはすぐに実行され(仕様に書いてある通り)、次に遅延が発生し、`9, 15, 20, 24...` となっています。 - -その制限は古代からあり、多くのスクリプトがそれに依存しているため、歴史的な理由から存在しています。 - -サーバサイド JavaScript では、その制限は存在しません。また、Node.JS では [process.nextTick](https://nodejs.org/api/process.html) や [setImmediate](https://nodejs.org/api/timers.html) のような即時非同期ジョブをスケジュールする他の方法も存在します。従って、この概念はブラウザ固有のものです。 -```` - -### ブラウザのレンダリングを許可する +最初のタイマーはすぐに実行され(仕様に書いてある通り)、次に遅延が発生し、`9, 15, 20, 24...` となっています。呼び出し間で4ミリ秒以上の必須の遅延が発生します。 -ブラウザ内でのスクリプトの別の利点は、プログレスバー等をユーザに表示できることです。これは、ブラウザは通常スクリプトが完了した後に全ての "再ペイント" をするためです。 +同様のことが、`setTimeout` の代わりに `setInterval` を使用する場合にも起きます: `setInterval(f)` は数回ゼロ遅延で `f` を実行し、その後 4ミリ秒以上の遅延で実行します。 -従って、私たちが1つの巨大な関数を実行し、そこで何かを変えたとしても、その変更は関数が終わるまでドキュメント上には反映されません。 +その制限は古くからあり、多くのスクリプトがそれに依存しているため、歴史的な理由から存在しています。 -これはそのデモです: -```html run -
- - -``` - -あなたがこれを実行した場合、 `i` の変更は count 全体が終わった後に行われます。 - -そして、`setTimeout` を使ってそれを小さく分割すると、変更は各実行の間で適用されます。なので、これは良く見えます: - -```html run -
- - -``` - -これで、`
` は `i` の値の増加を表示します。 +サーバサイド JavaScript では、その制限は存在しません。また、Node.js では [process.nextTick](https://nodejs.org/api/process.html) や [setImmediate](https://nodejs.org/api/timers.html) のような即時非同期ジョブをスケジュールする他の方法も存在します。従って、この概念はブラウザ固有のものです。 +```` ## サマリ - メソッド `setInterval(func, delay, ...args)` と `setTimeout(func, delay, ...args)` は、`delay` ミリ秒に `func` を定期的に/一度だけ実行することができます。 - 実行をキャンセルするためには、`setInterval/setTimeout` で返却された値と一緒に `clearInterval/clearTimeout` を呼ぶ必要があります。 - ネストされた `setTimeout` 呼び出しは、`setInterval` よりも柔軟です。また、それは実行 *間* の最小時間を保証することができます。 -- ゼロタイムアウトスケジューリング `setTimeout(...,0)` は "できるだけ早く、しかし現在のコードが終わった後に" 呼び出しをスケジュールするために使われます。 - -`setTimeout(...,0)` のいくつかのユースケースです: -- CPUを必要とするタスクを小さく分割するために、スクリプトが "ハングアップ" しないために。 -- 処理が進行している間にブラウザに何か他のことをさせるために(プログレスバーを描画するなど)。 +- `setTimeout(func, 0)` (`setTimeout(func` と同じ)での遅延ゼロのスケジューリングは "できるだけ早く、しかし現在のコードが終わった後に" 呼び出しをスケジュールするために使われます。 +- ブラウザは、5つ以上のネストされた `setTimeout` 呼び出し、または `setInterval`(5回目以降の呼び出し後)の最小遅延を4msに制限しています。それは歴史的な理由によるものです。 -すべてのスケジューリングメソッドは正確な遅延を *保証しない* ことに注意してください。スケジュールされたコードでは、それに頼るべきではありません。 +すべてのスケジューリングメソッドは正確な遅延を *保証しない* ことに注意してください。 例えば、ブラウザ内でのタイマーは、多くの理由で遅くなる可能性があります: - CPUが過負荷になっている diff --git a/1-js/06-advanced-functions/08-settimeout-setinterval/setinterval-interval.png b/1-js/06-advanced-functions/08-settimeout-setinterval/setinterval-interval.png deleted file mode 100644 index 060b2c293b..0000000000 Binary files a/1-js/06-advanced-functions/08-settimeout-setinterval/setinterval-interval.png and /dev/null differ diff --git a/1-js/06-advanced-functions/08-settimeout-setinterval/setinterval-interval.svg b/1-js/06-advanced-functions/08-settimeout-setinterval/setinterval-interval.svg new file mode 100644 index 0000000000..bce7d6a843 --- /dev/null +++ b/1-js/06-advanced-functions/08-settimeout-setinterval/setinterval-interval.svg @@ -0,0 +1 @@ +func(1)func(2)func(3)100200300 \ No newline at end of file diff --git a/1-js/06-advanced-functions/08-settimeout-setinterval/setinterval-interval@2x.png b/1-js/06-advanced-functions/08-settimeout-setinterval/setinterval-interval@2x.png deleted file mode 100644 index 4071849cd5..0000000000 Binary files a/1-js/06-advanced-functions/08-settimeout-setinterval/setinterval-interval@2x.png and /dev/null differ diff --git a/1-js/06-advanced-functions/08-settimeout-setinterval/settimeout-interval.png b/1-js/06-advanced-functions/08-settimeout-setinterval/settimeout-interval.png deleted file mode 100644 index 6c473a33cc..0000000000 Binary files a/1-js/06-advanced-functions/08-settimeout-setinterval/settimeout-interval.png and /dev/null differ diff --git a/1-js/06-advanced-functions/08-settimeout-setinterval/settimeout-interval.svg b/1-js/06-advanced-functions/08-settimeout-setinterval/settimeout-interval.svg new file mode 100644 index 0000000000..d6d233b2ba --- /dev/null +++ b/1-js/06-advanced-functions/08-settimeout-setinterval/settimeout-interval.svg @@ -0,0 +1 @@ +func(1)func(2)func(3)100100 \ No newline at end of file diff --git a/1-js/06-advanced-functions/08-settimeout-setinterval/settimeout-interval@2x.png b/1-js/06-advanced-functions/08-settimeout-setinterval/settimeout-interval@2x.png deleted file mode 100644 index dd45e324f8..0000000000 Binary files a/1-js/06-advanced-functions/08-settimeout-setinterval/settimeout-interval@2x.png and /dev/null differ diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/02-delay/solution.md b/1-js/06-advanced-functions/09-call-apply-decorators/02-delay/solution.md index f742440656..858a6f8315 100644 --- a/1-js/06-advanced-functions/09-call-apply-decorators/02-delay/solution.md +++ b/1-js/06-advanced-functions/09-call-apply-decorators/02-delay/solution.md @@ -12,7 +12,7 @@ function delay(f, ms) { ここで、アロー関数がどう使われているか注意してください。ご存知の通り、アロー関数は独自の `this` や `arguments` を持ちません。なので、`f.apply(this, arguments)` はラッパーから `this` と `arguments` を取ります。 -もし、通常の関数を渡す場合、`setTimeout` はそれを引数なしで、 `this=window` (ブラウザの場合) で呼び出します。なので、ラッパーからそれらをわすようにコードを書く必要があります。: +もし、通常の関数を渡す場合、`setTimeout` はそれを引数なしで、 `this=window` (ブラウザの場合) で呼び出します。なので、ラッパーからそれらを渡すようにコードを書く必要があります。: ```js function delay(f, ms) { diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/article.md b/1-js/06-advanced-functions/09-call-apply-decorators/article.md index 2412850998..424dc3fa64 100644 --- a/1-js/06-advanced-functions/09-call-apply-decorators/article.md +++ b/1-js/06-advanced-functions/09-call-apply-decorators/article.md @@ -1,15 +1,12 @@ # デコレータと転送, call/apply -JavaScriptでは関数を扱う際、非常に柔軟性があります。関数は渡され、オブジェクトとして使われます。また、これらの間の呼び出しを *転送* したり、それらを *装飾(デコレータ)* することもできます。ここではそれらの方法を見ていきましょう。 - - -[cut] +JavaScript は関数を処理する際、非常に高い柔軟性を提供します。関数は渡され、オブジェクトとして使われます。ここでは、これらの間の呼び出しを *転送* したり、*装飾(デコレータ)* する方法を説明します。 ## 透過キャッシュ(Transparent caching) -CPU負荷は高いが、その結果が不変である関数 `slow(x)` を持っているとします。言い換えると、同じ `x` の場合、常に同じ結果が返ってきます。 +CPU負荷は高いが、その結果が不変である関数 `slow(x)` があるとします。言い換えると、同じ `x` の場合、常に同じ結果が返ってきます。 -もし関数が頻繁に呼ばれた場合、再計算に余分な時間を費やすことを避けるために、異なる `x` の結果をキャッシュ(覚えておく)して欲しいかもしれません。 +もし関数が頻繁に呼ばれる場合、再計算に余分な時間を費やすことを避けるため、結果をキャッシュ(覚えておく)して欲しいかもしれません。 その機能を `slow()` に追加する代わりに、ラッパーを作りましょう。これから見ていくように、そうすることで多くのメリットがあります。 @@ -52,11 +49,9 @@ alert( "Again: " + slow(2) ); // 前の行と同じ メインの関数コードからキャッシュ機能を分離することで、コードをシンプルに保つこともできます。 -さて、それがどのように動作するのか詳細を見ていきましょう: - `cachingDecorator(func)` の結果は "ラッパー" です: `func(x)` の呼び出しをキャッシュロジックに "ラップ" する `function(x)` です。: -![](decorator-makecaching-wrapper.png) +![](decorator-makecaching-wrapper.svg) 上でわかるように、ラッパーは `func(x)` の結果を "そのまま" 返します。外部のコードからは、ラップされた `slow` 関数は、依然として同じことを行い、単にその振る舞いに追加されたキャッシュの側面をもちます。 @@ -66,7 +61,6 @@ alert( "Again: " + slow(2) ); // 前の行と同じ - キャッシュロジックは分離されているので、`slow` 自身の複雑性は増加しません。 - 必要に応じて、複数のデコレータを組み合わせることができます(他のデコレータについては次に続きます)。 - ## コンテキストのために、"func.call" を利用する 上で言及されたキャッシュデコレータはオブジェクトメソッドで動作するのには適していません。 @@ -113,10 +107,9 @@ alert( worker.slow(2) ); // Whoops! Error: Cannot read property 'someMethod' of 行 `(*)` で、`this.someMethod` にアクセスしようとして失敗してエラーが起きます。なぜかわかりますか? -理由は、ラッパーは行 `(**)` でオリジナル関数を `func(x)` として呼び出すためです。また、そのように呼び出した場合、関数は `this = undefined` となります。 - +理由は、ラッパーは行 `(**)` でオリジナル関数を `func(x)` として呼び出すためです。また、このように呼び出した場合、関数は `this = undefined` となります。 -次のコードを実行しようとすると、同様の現象が観察されます: +次のコードを実行しようとすると、同様の現象が見られます: ```js let func = worker.slow; @@ -176,7 +169,6 @@ say.call( user, "Hello" ); // John: Hello 我々のケースでは、オリジナルの関数にコンテキストを渡すため、ラッパーの中で `call` を使うことができます。: - ```js run let worker = { someMethod() { @@ -217,8 +209,7 @@ alert( worker.slow(2) ); // 動作します(キャッシュが使われます 2. なので、`worker.slow(2)` が実行されるとき、ラッパーは引数として `2` と、`this=worker` を取ります(これはドットの前のオブジェクトになります)。 3. ラッパーの中では、 結果がまだキャッシュされていないと仮定すると、`func.call(this, x)` はオリジナルのメソッドに現在の `this` (`=worker`) と、現在の引数 (`=2`)を渡します。 - -## "func.apply" で複数の引数を使用する +## 複数の引数 さて、`cachingDecorator` をより普遍的なものにしましょう。これまでは、単一引数の関数でのみ動作していました。 @@ -235,9 +226,7 @@ let worker = { worker.slow = cachingDecorator(worker.slow); ``` -ここでは、それを解決するための2つのタスクがあります。 - -最初は、`cache` マップのキーに、`min` と `max` 両方の引数を使う方法です。以前は、1つの引数 `x` に対し、単に `cache.set(x, result)` として結果を保存し、`cache.get(x)` でそれを取得していました。しかし、今回は *引数の組み合わせ* `(min,max)` で結果を覚える必要があります。ネイティブの `Map` は1つのキーに1つの値のみを取ります。 +以前は、1つの引数 `x` に対し、単に `cache.set(x, result)` として結果を保存し、`cache.get(x)` でそれを取得していました。しかし、今回は *引数の組み合わせ* `(min,max)` で結果を覚える必要があります。ネイティブの `Map` は1つのキーに1つの値のみを取ります。 可能な解決策はたくさんあります: @@ -247,81 +236,9 @@ worker.slow = cachingDecorator(worker.slow); 実用的な多くのアプリケーションでは、第3の策で十分ですので、それで進めます。 -解決するための2つ目のタスクは、多くの引数を `func` に渡す方法です。現在ラッパー `function(x)` は1つの引数を想定しており、`func.call(this, x)` はそれを渡します。 - -ここで、私たちは別の組み込みメソッド [func.apply](mdn:js/Function/apply) を使うことができます。 - -構文は次の通りです: - -```js -func.apply(context, args) -``` - -これは、`this=context` として設定し、引数のリストとして配列ライクなオブジェクト `args` を使って `func` を実行します。 - -例えば、これら2つの呼び出しはほぼ同じです: - -```js -func(1, 2, 3); -func.apply(context, [1, 2, 3]) -``` - -両方とも、引数 `1,2,3` が与えられた `func` を実行します。しかし、`apply` は `this=context` のセットもします。 - -例えば、`this=user` と引数のリストとして `messageData` で `say` が呼ばれると次のようになります: - -```js run -function say(time, phrase) { - alert(`[${time}] ${this.name}: ${phrase}`); -} - -let user = { name: "John" }; - -let messageData = ['10:00', 'Hello']; // time と phrase になります - -*!* -// user は this になり, messageData は引数のリストとして渡されます (time, phrase) -say.apply(user, messageData); // [10:00] John: Hello (this=user) -*/!* -``` - -`call` と `apply` の構文の唯一の違いは、`call` は引数のリストを期待し、`apply` はそれらの配列ライクなオブジェクトを期待している点です。 - -私たちは既に、チャプター  で、引数のリストとして配列 (もしくは任意の頒布可能(iterable)) を渡すことのできるスプレッド演算子 `...` を知っています。`call` でそれを使う場合、`apply` とほぼ同じ結果になります。 - -これら2つの呼び出しはほぼほぼ同等です: - -```js -let args = [1, 2, 3]; - -*!* -func.call(context, ...args); // スプレッド演算子として配列を渡すことは -func.apply(context, args); // apply を使うのと同じです -*/!* -``` - -より詳しく見ると、 `call` と `apply` の使い方には若干の違いがあります。 - -- スプレッド演算子 `...` は `call` へのリストとして *反復可能(iterable)* な `args` を渡すことができます。 -- `apply` は *配列ライク(array-like)* な `args` のみを許可します。 - -したがって、これらの呼び出しはお互いを補完します。 反復可能(iterable) が期待されるところでは、 `call` が動作します。配列ライク(array-like)が期待される場合は `apply` が動作します。 - -また、`args` が 反復可能であり配列ライクである場合は、本当の配列のように、技術的にはどちらを使うことも可能ですが、`apply` は恐らくより高速です。なぜなら、1つの操作だからです。ほとんどのJavaScriptエンジンの内部の最適化は、 `call + spread` のペアよりも良いものです。 - -`apply` の最も重要な用途の1つは、次のように別の関数へ呼び出しを渡すことです。: - -```js -let wrapper = function() { - return anotherFunction.apply(this, arguments); -}; -``` - -これは *呼び出し転送(call forwarding)* と呼ばれます。 `wrapper` はコンテキスト `this` と `anotherFunction` への引数を取得し、その結果の戻り値を返します。 - -このような `wrapper` を外部コードが呼び出すと、元の関数の呼び出しと区別できなくなります。 +また、`func.call` で `x` だけでなくすべての引数を渡す必要があります。`function()` の中で、 `arguments` として引数の疑似配列を取得できるので、`func.call(this,x)` を `func.call(this, ...arguments)` に置き換えます。 -今度はそれをもっと強力な `cachingDecorator` にしましょう。: +これはより強力な `cachingDecorator` です: ```js run let worker = { @@ -342,7 +259,7 @@ function cachingDecorator(func, hash) { } *!* - let result = func.apply(this, arguments); // (**) + let result = func.call(this, ...arguments); // (**) */!* cache.set(key, result); @@ -360,13 +277,54 @@ alert( worker.slow(3, 5) ); // works alert( "Again " + worker.slow(3, 5) ); // same (cached) ``` -これで、ラッパー任意の数の引数で動作します。 +これで任意の数の引数でも動作します(ただし、ハッシュ関数も任意の数の引数を許可するように調整する必要があります。これを処理する興味深い方法については、以下で説明します)。 + +2つの変更があります: + +- 行 `(*)` では、`hash` を呼び出し、`arguments` から単一のキーを生成しています。ここではシンプルな "結合" 関数を使用しています(引数 `(3, 5)` をキー `"3,5"` にする)。より複雑なケースでは他のハッシュ関数を必要とするかもしれません。 +- その後、`(**)` で`func.call(this, ...arguments)` を使用して、コンテキストとラッパーが取得したすべての引数をオリジナルの関数に渡しています。 + +### func.apply + +`func.call(this, ...arguments)` の代わりに `func.apply(this, arguments)` を利用することができます。 + +組み込みメソッド [func.apply](mdn:js/Function/apply) の構文は以下の通りです: + +```js +func.apply(context, args) +``` + +これは、`this=context` として設定し、引数のリストとして配列ライクなオブジェクト `args` を使って `func` を実行します。 + +`call` と `apply` の構文の唯一の違いは、`call` は引数のリストを期待し、`apply` はそれらの配列ライクなオブジェクトを期待している点です。 + +そのため、これら2つの呼び出しはほぼ同等です: + +```js +func.call(context, ...args); +func.apply(context, args); +``` + +これらは与えられたコンテキストと引数で `func` 呼び出しを実行します。 + +`apply` の使い方には若干の違いがあります。 + +- スプレッド演算子 `...` は `call` へのリストとして *反復可能(iterable)* な `args` を渡すことができます。 +- `apply` は *配列ライク(array-like)* な `args` のみを許可します。 -2つの変更をしています: +...また、実際の配列など、反復可能かつ配列ライクなオブジェクトの場合は、任意のいずれかを使用できますが、多くの JavaScript エンジンで内部の最適化がされるため、`apply` の方が恐らく高速です。 -- 行 `(*)` では、`arguments` から1つのキーを作成するために `hash` を呼び出しています。ここでは、引数 `(3, 5)` をキー `"3,5"` に変換する単純な "結合" 関数を使います。より複雑なケースでは他のハッシュ関数が必要になる場合もあります。 -- 次に `(**)` でラッパーが取得したコンテキストとすべての引数(どれだけ多くても問題ありません)を元の関数に渡すために `func.apply` を使っています。 +コンテキストと一緒にすべての引数を別の関数にわたすことは、*call forwarding(呼び出しの転送)* と言われます。 +最もシンプルな形は以下です: + +```js +let wrapper = function() { + return func.apply(this, arguments); +}; +``` + +外部のコードがこのような `wrapper` は呼び出すと、元の関数 `func` の呼び出しと見分けはつきません。 ## メソッドの借用(Borrowing a method) @@ -434,12 +392,20 @@ hash(1, 2); 従って、技術的には `this` を取り、`this[0]`, `this[1]` ... などを一緒に結合します。これは意図的に任意の配列ライク(array-like) の `this` を許容する方法で書かれています(多くのメソッドがこの慣習に従っています)。そういうわけで `this=arguments` でも動きます。 +## デコレータと関数プロパティ + +一般的に、1つの小さい点を除けば、デコレートされている関数やメソッドを置き換えるのは安全です。もしオリジナルの関数に `func.calledCount` といったようなプロパティが含まれている場合、デコレートされた関数はラッパーであるためそれらは提供しません。したがって、それらを利用する際には注意が必要です。 + +E.g. 上記の例では、`slow` 関数にあるプロパティが含まれている場合、`cachingDecorator(slow)` はそれを持たないラッパーです。 + +一部のデコレータは独自のプロパティを提供することがあります。E.g. 関数が何回実行されたか、どれだけ時間がかかったかをカウントし、ラッパープロパティを介してこの情報を公開する、といったケースです。 + +関数のプロパティへのアクセスを維持するデコレータを作成する方法はありますが、これには、関数をラップするために特別な `Proxy` オブジェクトを使用する必要があります。これについては、後ほど で説明します。 + ## サマリ *デコレータ* は関数の振る舞いを変更するラッパーです。メインの仕事は引き続き元の関数により行われます。 -小さい点を除けば、デコレートされた関数またはメソッドへの置き換えは安全です。もし `func.calledCount` のように元の関数がプロパティを持っている場合、デコレートされた関数はそれらを提供しません。なぜなら、それはラッパーだからです。従って、使用する場合は注意する必要があります。 一部のデコレータは独自のプロパティを提供します。 - デコレータは、関数に追加できる「機能」または「特徴」として見ることができます。 1つを追加したり、より多く追加することができます。 そして、これらすべてコードを変更することなく行うことができます! `cachingDecorator` を実装するために、次のメソッドを学びました: @@ -455,6 +421,6 @@ let wrapper = function() { } ``` -また、私たちはオブジェクトからメソッドを取得し、別のオブジェクトのコンテキストでそのメソッドを `呼び出す` と言う、 *メソッドの借用* の例も見ました。配列のメソッドを取り、それらを引数に適用するのはよくあることです。代替としては、本当の配列である残りのパラメータオブジェクトを使うこと、があります。 +また、私たちはオブジェクトからメソッドを取得し、別のオブジェクトのコンテキストでそのメソッドを呼び出すと言う、 *メソッドの借用* の例も見ました。配列のメソッドを取り、それらを引数 `arguments` に適用するのはよくあることです。代替としては、本当の配列である残りのパラメータオブジェクトを使うこと、があります。 多くのデコレータが世の中に出回っています。このチャプターのタスクを解決することで、いかにデコレータが良いものかを確認してください。 diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/decorator-makecaching-wrapper.png b/1-js/06-advanced-functions/09-call-apply-decorators/decorator-makecaching-wrapper.png deleted file mode 100644 index 171e27910d..0000000000 Binary files a/1-js/06-advanced-functions/09-call-apply-decorators/decorator-makecaching-wrapper.png and /dev/null differ diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/decorator-makecaching-wrapper.svg b/1-js/06-advanced-functions/09-call-apply-decorators/decorator-makecaching-wrapper.svg new file mode 100644 index 0000000000..9b63cb982b --- /dev/null +++ b/1-js/06-advanced-functions/09-call-apply-decorators/decorator-makecaching-wrapper.svg @@ -0,0 +1 @@ +wrapperaround the function \ No newline at end of file diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/decorator-makecaching-wrapper@2x.png b/1-js/06-advanced-functions/09-call-apply-decorators/decorator-makecaching-wrapper@2x.png deleted file mode 100644 index 8d3b5434c2..0000000000 Binary files a/1-js/06-advanced-functions/09-call-apply-decorators/decorator-makecaching-wrapper@2x.png and /dev/null differ diff --git a/1-js/06-advanced-functions/10-bind/article.md b/1-js/06-advanced-functions/10-bind/article.md index 36380222f3..d8b17ef2a9 100644 --- a/1-js/06-advanced-functions/10-bind/article.md +++ b/1-js/06-advanced-functions/10-bind/article.md @@ -5,12 +5,10 @@ libs: # 関数バインディング -オブジェクトメソッドで `setTimeout` 使ったり、オブジェクトメソッドを渡すような場合、"`this` を失う" という既知の問題があります。 +オブジェクトメソッドで `setTimeout` を使ったり、オブジェクトメソッドを渡すような場合、"`this` を失う" という既知の問題があります。 突然、`this` が正しく動作するのをやめます。この状況は初心者の開発者には典型的ですが、経験者でも同様に起こりえます。 -[cut] - ## "this" を失う 私たちはすでに、JavaScriptでは `this` を失うことが容易であることを知っています。 あるメソッドがオブジェクトから別の場所に渡されると、`this` は失われます。 @@ -39,7 +37,7 @@ let f = user.sayHi; setTimeout(f, 1000); // user コンテキストを失います ``` -ブラウザにおいて、メソッド `setTimeout` は少し特別です: 関数呼び出しでは `this=window` を設定します(Node.JS では、`this` はタイマーオブジェクトになりますが、ここではほとんど関係ありません)。従って、`this.firstName` は、存在しない `window.firstName` を取得しようとします。他の同様のケースでは、通常 `this` は `undefined` になります。 +ブラウザにおいて、メソッド `setTimeout` は少し特別です: 関数呼び出しでは `this=window` を設定します(Node.js では、`this` はタイマーオブジェクトになりますが、ここではほとんど関係ありません)。従って、`this.firstName` は、存在しない `window.firstName` を取得しようとします。他の同様のケースでは、通常 `this` は `undefined` になります。 このタスクは非常に典型的です -- オブジェクトメソッドをどこか別の場所(ここではスケジューラに渡して)から呼び出したい場合です。それが適切なコンテキストで呼び出されることはどのように確認すればよいでしょう? @@ -86,7 +84,9 @@ let user = { setTimeout(() => user.sayHi(), 1000); // ...1秒以内に次が行われると -user = { sayHi() { alert("Another user in setTimeout!"); } }; +user = { + sayHi() { alert("Another user in setTimeout!"); } +}; // Another user in setTimeout?!? ``` @@ -102,7 +102,7 @@ user = { sayHi() { alert("Another user in setTimeout!"); } }; ```js // より複雑な構文はもう少し後で let boundFunc = func.bind(context); -```` +``` `func.bind(context)` の結果は特別な関数ライクな "エキゾチックオブジェクト(exotic object)" です。これは関数として呼ぶことができ、`func` に `this=context` を透過的に渡します。 @@ -161,9 +161,16 @@ let user = { let sayHi = user.sayHi.bind(user); // (*) */!* +// オブジェクトなしで実行可能 sayHi(); // Hello, John! setTimeout(sayHi, 1000); // Hello, John! + +// 1秒以内に user の値が変わったとしても +// sayHi は古い user オブジェクトを参照しているバインド前の値を使用します +user = { + sayHi() { alert("Another user in setTimeout!"); } +}; ``` `(*)` の行で、メソッド `user.sayHi` を `user` にバインドしています。`sayHi` は "束縛(バインド)された" 関数であり、単独もしくは `setTimeout` に渡して呼び出すことができます。 @@ -195,11 +202,127 @@ for (let key in user) { } ``` -JavaScriptライブラリはまた、便利な大量バインディングのための機能も提供しています。e.g. [_.bindAll(obj)](http://lodash.com/docs#bindAll) in lodash. +JavaScriptライブラリは、便利な多数のバインドを行うための機能も提供しています。e.g. [_.bindAll(obj)](http://lodash.com/docs#bindAll) in lodash. ```` +## 部分関数 + +これまでは、`this` のバインドについてのみ説明してきました。次のステップにいきましょう。 + +`this` だけでなく、引数もバインドすることが可能です。これはめったにされませんが、便利な場合があります。 + +`bind` の完全な構文は次の通りです: + +```js +let bound = func.bind(context, [arg1], [arg2], ...); +``` + +context を `this` とし、関数の開始引数をバインドすることができます。 + +例えば、乗算関数 `mul(a, b)` があるとします: + +```js +function mul(a, b) { + return a * b; +} +``` + +これをベースに、`bind` を使用して、`double` 関数を作成しましょう。: + +```js run +function mul(a, b) { + return a * b; +} + +*!* +let double = mul.bind(null, 2); +*/!* + +alert( double(3) ); // = mul(2, 3) = 6 +alert( double(4) ); // = mul(2, 4) = 8 +alert( double(5) ); // = mul(2, 5) = 10 +``` + +`mul.bind(null, 2)` の呼び出しで新しい関数 `double` を作成し、これはコンテキストを `null`、最初の引数を `2` で固定した `mul` を呼び出します。それ以降の引数は "そのまま" 渡されます。 + +これは [部分関数アプリケーション](https://en.wikipedia.org/wiki/Partial_application) と呼ばれ、既存のパラメータのいくつかを固定にすることで新しい関数を作成します。 + +実際にはここでは `this` は使用しないことに留意してください。ですが、`bind` で指定が必要なので `null` など何かしらを置く必要があります。 + +以下のコードの関数 `triple` は値を3倍します。: + +```js run +function mul(a, b) { + return a * b; +} + +*!* +let triple = mul.bind(null, 3); +*/!* + +alert( triple(3) ); // = mul(3, 3) = 9 +alert( triple(4) ); // = mul(3, 4) = 12 +alert( triple(5) ); // = mul(3, 5) = 15 +``` + +なぜ部分関数を作るのでしょうか? + +メリットは、読みやすい名前(`double`, `triple`)で独立した関数を作ることができることです。`bind` で固定されているため、毎回最初の引数を指定する必要がありません。 + +他のケースでは、非常に一般的な関数がある状態で、便利さのために特定用途のパターンが欲しい場合に部分関数は役立ちます。 + +例えば、関数 `send(from, to, text)` があるとします。`user` オブジェクトの中で、その部分パターンを使用したい場合、現在のユーザから送信をする関数 `sendTo(to, text)` 。 + +## Going partial without context + +仮に引数のいくつかを固定したいが、コンテキスト `this` は固定したくない場合はどうしますか?例えば、オブジェクトメソッドです。 + +ネイティブの `bind` はそれは許可しません。コンテキストを省略して引数だけ指定することはできません。 + +幸いなことに、引数だけをバインドするための関数 `partial` は簡単に実装できます。 + +次のようになります: + +```js run +*!* +function partial(func, ...argsBound) { + return function(...args) { // (*) + return func.call(this, ...argsBound, ...args); + } +} +*/!* + +// Usage: +let user = { + firstName: "John", + say(time, phrase) { + alert(`[${time}] ${this.firstName}: ${phrase}!`); + } +}; + +// 固定時間で部分メソッドを追加 +user.sayNow = partial(user.say, new Date().getHours() + ':' + new Date().getMinutes()); + +user.sayNow("Hello"); +// Something like: +// [10:00] John: Hello! +``` + +`partial(func[, arg1, arg2...])`呼び出しの結果は以下をもつ `func` を呼び出すラッパー `(*)`です。 +- 取得したものと同じ `this`(`user.sayNow` 呼び出しの場合、`user`) +- 次に `...argsBound` を指定します。`partial` 呼び出しからの引数 (`"10:00"`) +- 次に `...args`。ラッパーに与えられた引数(`"Hello"`) + +なので、スプレッド構文で簡単に行うことができます。 + +また、lodash ライブラリでは、[_.partial](https://lodash.com/docs#partial) 実装が用意されています。 + ## サマリ メソッド `func.bind(context, ...args)` はコンテキスト `this` を固定した関数 `func` の "束縛されたバリアント" を返します。 -通常は、オブジェクトメソッドで `this` を固定するために `bind` を適用し、どこかに渡すことができるようにします。たとえば、`setTimeout` に。 近代的な開発で "束縛する" 理由はまだまだありますが、私たちは後でそれらを知るでしょう。 +通常は、オブジェクトメソッドで `this` を固定するために `bind` を適用し、どこか (たとえば `setTimeout`) に渡すことができるようにします。 + +既存の関数において、引数のいくつかを固定した汎用性の低い関数は、部分関数と呼ばれます。 + +部分関数は同じ引数を何度も繰り返したくないときに便利です。例えば、`send(from, to)` という関数があって、`from`はどのタスクでも常に同じになるはずなら、部分関数を利用してそれを処理できます。 diff --git a/1-js/06-advanced-functions/11-currying-partials/1-ask-currying/task.md b/1-js/06-advanced-functions/11-currying-partials/1-ask-currying/task.md deleted file mode 100644 index 8149792ef1..0000000000 --- a/1-js/06-advanced-functions/11-currying-partials/1-ask-currying/task.md +++ /dev/null @@ -1,34 +0,0 @@ -importance: 5 - ---- - -# ログイン用の部分的なアプリケーション - -このタスクは 少しより複雑なバリアントです。 - -`user` オブジェクトが修正されました。今、2つの関数 `loginOk/loginFail` の代わりに、単一の関数 `user.login(true/false)` があります。 - -下のコードでは、何を渡すと `ok` として `user.login(true)` を、`fail` として `user.login(fail)` を呼ぶでしょうか? - -```js -function askPassword(ok, fail) { - let password = prompt("Password?", ''); - if (password == "rockstar") ok(); - else fail(); -} - -let user = { - name: 'John', - - login(result) { - alert( this.name + (result ? ' logged in' : ' failed to log in') ); - } -}; - -*!* -askPassword(?, ?); // ? -*/!* -``` - -変更はハイライトされた箇所の修正だけにしてください。 - diff --git a/1-js/06-advanced-functions/11-currying-partials/article.md b/1-js/06-advanced-functions/11-currying-partials/article.md deleted file mode 100644 index ca3fec89c6..0000000000 --- a/1-js/06-advanced-functions/11-currying-partials/article.md +++ /dev/null @@ -1,299 +0,0 @@ -libs: - - lodash - ---- - -# カリー化と部分適用 - -今まで、`this` をバインドすることについて話していました。 さあ、もう一歩を進めましょう。 - -私たちは、`this` だけでなく、引数もバインドすることができます。それはめったにされませんが、便利なときがあります。 - -[cut] - -`bind` の完全な構文です: - -```js -let bound = func.bind(context, arg1, arg2, ...); -``` - -これは、コンテキスト( `this` として)と、関数の開始引数をバインドすることができます。 - -例えば、乗算関数 `mul(a, b)` を考えます: - -```js -function mul(a, b) { - return a * b; -} -``` - -これをベースとした関数 `double` を作るために、`bind` を使ってみましょう。: - -```js run -*!* -let double = mul.bind(null, 2); -*/!* - -alert( double(3) ); // = mul(2, 3) = 6 -alert( double(4) ); // = mul(2, 4) = 8 -alert( double(5) ); // = mul(2, 5) = 10 -``` - -`mul.bind(null, 2)` を呼び出すと、`mul` を呼び出す新しい関数 `double` が作成されます。それは、`null` がコンテキストとして、`2` が最初の引数として固定されます。さらに、その他の引数は "そのまま" 渡されます。 - -これは [関数への部分的な適用(partial function application)](https://en.wikipedia.org/wiki/Partial_application)と呼ばれます -- 既存の関数の一部のパラメータを変更することで新しい関数を作ります。 - -ここで、例では実際には `this` を使っていないことに注意してください。しかし `bind` はそれを必要とするため、`null` のような何かを指定する必要があります。 - -下のコードの関数 `triple` は値を3倍にします: - -```js run -*!* -let triple = mul.bind(null, 3); -*/!* - -alert( triple(3) ); // = mul(3, 3) = 9 -alert( triple(4) ); // = mul(3, 4) = 12 -alert( triple(5) ); // = mul(3, 5) = 15 -``` - -なぜ、通常部分的な関数を作るのでしょうか? - -ここでの我々のメリットは、分かりやすい名前(`double`, `triple`)で独立した関数を作れたことです。私たちはそれを使うことができ、毎回最初の引数を書く必要がありません。なぜなら、`bind` で固定されているからです。 - -別のケースでは、非常に汎用的な関数を持っており、利便性のために汎用性を減らしたい時に部分適用は役立ちます。 - -例えば、関数 `send(from, to, text)` を考えます。次に、`user` オブジェクトの内側で、その部分的なバリアントを使いたいかもしれません。: 現在のユーザから送信を行う `sendTo(to, text)` 関数など。 - - -## コンテキストなしの部分適用 - -仮に、いくつかの引数を修正したいが、`this` をバインドしない場合はどうなるでしょう? - -ネイティブの `bind` ではそれは許可されていません。単にコンテキストを省略し引数にジャンプすることは出来ません。 - -ただ、幸いにも引数だけをバインドする `partial` 関数は簡単に実装することが出来ます。 - -このようになります: - -```js run -*!* -function partial(func, ...argsBound) { - return function(...args) { // (*) - return func.call(this, ...argsBound, ...args); - } -} -*/!* - -// 使い方: -let user = { - firstName: "John", - say(time, phrase) { - alert(`[${time}] ${this.firstName}: ${phrase}!`); - } -}; - -// 最初の引数を固定して何かを表示する部分的なメソッドを追加する -user.sayNow = partial(user.say, new Date().getHours() + ':' + new Date().getMinutes()); - -user.sayNow("Hello"); -// このようになります: -// [10:00] Hello, John! -``` - -`partial(func[, arg1, arg2...])` の呼び出しの結果は次のように `func` を呼び出すラッパー `(*)` です。: -- `this` はそれが取得したものと同じです(`user.sayNow` の場合 `user` です)。 -- 次に、`...argsBound` を与えます -- `partial` 呼び出しからの引数(`"10:00"`)です。 -- 次に、`...args` です -- ラッパーへ与えられた引数(`"Hello"`) です。 - -なのでスプレッド演算子を使えば簡単ですね。 - -また、lodashライブラリの [_.partial](https://lodash.com/docs#partial) 実装も用意されています。 - -## カリー化(Currying) - -"カリー化" と呼ばれる別のものと、上で言及された関数の部分適用を混同する人もいます。それらはここで言及しておくべき関数を扱う別の興味深いテクニックです。 - -[Currying](https://en.wikipedia.org/wiki/Currying) は `f(a, b, c)` と呼び出し可能なものを `f(a)(b)(c)` として呼び出しできるように変換します。 - -2変数関数に対するカリー化を行う関数 `curry` を作ってみましょう。つまり、`f(a, b)` を `f(a)(b)` に変換します。: - -```js run -*!* -function curry(func) { - return function(a) { - return function(b) { - return func(a, b); - }; - }; -} -*/!* - -// 使い方 -function sum(a, b) { - return a + b; -} - -let carriedSum = curry(sum); - -alert( carriedSum(1)(2) ); // 3 -``` - -上でわかるように、実装はラッパーの連続です。 - -- `curry(func)` の結果はラッパー `function(a)` です。 -- `sum(1)` のように呼ばれるとき、引数はレキシカル環境に保存され、新しいラッパー `function(b)` が返却されます。 -- そして、最終的に `sum(1)(2)` は `2` で `function(b)` を呼び、それは元の複数引数を取る `sum` を呼びます。 - -lodash の [_.curry](https://lodash.com/docs#curry) のようなカリー化のより高度な実装は、より洗練された処理を行います。それらの関数は全ての引数が提供された場合には関数が正常に呼び出されるようなラッパーを返し、*そうでない場合* には、部分適用を返します。 - -```js -function curry(f) { - return function(..args) { - // もし args.length == f.length の場合(f と同じ引数がある場合), - // 呼び出しを f へ渡す - // それ以外は argsを最初の引数として固定する部分関数を返す - }; -} -``` - -## カリー化? 何のために? - -高度なカリー化を使用すると、簡単に関数を通常呼び出し可能にしつつ、部分適用をすることができます。このメリットを理解するために、価値のある実例を見る必要があります。 - -例えば、情報を整形して出力するロギング関数 `log(date, importance, message)` を持っているとします。実際のプロジェクトでは、このような関数には、ネットワーク経由での送信やフィルタリングなど、他にも多くの便利な機能があります。 - -```js -function log(date, importance, message) { - alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`); -} -``` - -では、カリー化してみましょう! - -```js -log = _.curry(log); -``` - -この処理の後でも `log` は通常の方法で動きます: - -```js -log(new Date(), "DEBUG", "some debug"); -``` - -...しかしカリー化された形式でも呼び出すことができます: - -```js -log(new Date())("DEBUG")("some debug"); // log(a)(b)(c) -``` - -今日のログのための便利な関数を取得してみましょう: - -```js -// todayLog は最初の引数が固定された log の一部になります -let todayLog = log(new Date()); - -// 使ってみます -todayLog("INFO", "message"); // [HH:mm] INFO message -``` - -また、これで今日のデバッグメッセージのための便利関数ができます: - -```js -let todayDebug = todayLog("DEBUG"); - -todayDebug("message"); // [HH:mm] DEBUG message -``` - -なので: -1. カリー化をしても何も失いませんでした。: `log` は以前のように呼び出し可能です。 -2. 色んなケースに応じて便利な部分適用した関数を生成する事ができました。 - - -## 高度なカリー実装 - -ここでは、上記で使用できる "高度な" カリー実装を示します。 - -```js run -function curry(func) { - - return function curried(...args) { - if (args.length >= func.length) { - return func.apply(this, args); - } else { - return function(...args2) { - return curried.apply(this, args.concat(args2)); - } - } - }; - -} - -function sum(a, b, c) { - return a + b + c; -} - -let curriedSum = curry(sum); - -// 通常通り呼ぶことも出来ます -alert( curriedSum(1, 2, 3) ); // 6 - -// curried(1)で部分を取得し、他の2つの引数で呼び出す -alert( curriedSum(1)(2,3) ); // 6 - -// 完全にカリー化された呼び出し -alert( curriedSum(1)(2)(3) ); // 6 -``` - -新しい `curry` は複雑に見えますが、実際には理解するのはとても簡単です。 - -`curry(func)` の結果は、このように `curried` のラッパーです。: - -```js -// func is the function to transform -function curried(...args) { - if (args.length >= func.length) { // (1) - return func.apply(this, args); - } else { - return function pass(...args2) { // (2) - return curried.apply(this, args.concat(args2)); - } - } -}; -``` - -これを実行すると、2つの分岐があります。: - -1. 渡された `args` の数と、元の関数で定義されている引数の数が同じ (`func.length`) かより多い場合、単にそれを呼び出し `func` に渡します。 -2. 部分適用を得る: そうでない場合は、`func` はまだ呼ばれません。代わりに別のラッパー `pass` が返却されます。これは `curried` を再度適用して新しい引数と一緒に前の引数を提供します。その後、新しい呼び出しでは、新しい部分適用(引数が不十分な場合)か、最終的に結果が得られます。 - -例えば、`sum(a, b, c)` のケースで何が起きるのかを見てみましょう。3つの引数があるので、`sum.length = 3` です。 - -`curried(1)(2)(3)` に対しては次のようになります: - -1. 最初の呼び出し `curried(1)` はそのレキシカル環境に `1` を覚え、ラッパー `pass` を返します。 -2. ラッパー `pass` は `(2)` で呼び出されます: それは前の引数(`1`)を取り、渡された `(2)` と連結し `curried(1, 2)` を呼び出します。引数の数としては、以前 3 より少ないので、`curry` は `pass` を返します。 -3. ラッパー `pass` は再び `(3)` で呼ばれ、次は `pass(3)` が以前の引数 (`1`, `2`) を取り、`3` を追加して `curried(1, 2, 3)` を呼び出します。 -- ついに引数が `3` となったので、それらは元の関数に渡されます。 - -もしまだ不明瞭であれば、心の中、あるいは紙に呼び出しシーケンスをトレースしてみると良いです。 - -```smart header="固定長の関数のみ" -カリー化では、関数が固定数の引数を持つ必要があります。 -``` - -```smart header="カリー化よりもさらに" -定義上、カリー化は `sum(a, b, c)` を `sum(a)(b)(c)` に変換するべきです。 - -しかし、JavaScriptでのカリー化のほとんどの実装は説明されているように高度であり、複数引数のバリアントでも関数が呼び出し可能となっています。 -``` - -## サマリ - -- 私たちが既存の関数のいくつかの引数を修正するとき、結果となる(汎用さは減る)関数は、*部分適用* と呼ばれます。部分適用を得るために `bind` を使うことができますが、他の方法でも可能です。 - - 同じ引数を何度も繰り返し指定したくないとき、部分適用は便利です。それは、私たちが `send(from, to)` 関数を持っていて, - `from` が常に同じになるような場合です。部分適用を得て処理を続けることができます。 - -- *カリー化* は `f(a,b,c)` を `f(a)(b)(c)` として呼び出し可能に変換します。JavaScriptの実装は、通常の形で呼び出し可能な関数を維持し、かつ引数が不足している場合には部分適用を返します。 - - 簡単な部分適用がほしいときにカリー化は素晴らしいです。ロギングの例で見てきたように、カリー化後の汎用的な関数 `log(date, importance, message)` は、1つの引数 `log(date)` または2つの引数 `log(date, importance)` で呼び出された時には部分適用を返します。 diff --git a/1-js/06-advanced-functions/12-arrow-functions/article.md b/1-js/06-advanced-functions/12-arrow-functions/article.md index 2d8e620366..a60999592f 100644 --- a/1-js/06-advanced-functions/12-arrow-functions/article.md +++ b/1-js/06-advanced-functions/12-arrow-functions/article.md @@ -2,9 +2,7 @@ アロー関数について改めて考えてみましょう。 -[cut] - -アロー関数は小さなものを書くための単なる "簡略化" ではありません。 +アロー関数は小さなものを書くための単なる "簡略化" ではありません。役立つ特徴がいくつかあります。 JavaScriptは、小さな関数を書く必要がある状況に満ちており、それはいろんな場所で実行されます。 @@ -16,7 +14,7 @@ JavaScriptは、小さな関数を書く必要がある状況に満ちており 関数を作成してどこかに渡すのは、JavaScriptの真髄です。 -そして、このような関数では、私たちは通常現在のコンテキストから離れたくありません。 +そして、このような関数では、私たちは通常現在のコンテキストから離れたくありません。アロー関数はこのようなとき便利です。 ## アロー関数は "this" を持っていません diff --git a/1-js/07-object-oriented-programming/01-property-descriptors/article.md b/1-js/07-object-oriented-programming/01-property-descriptors/article.md deleted file mode 100644 index bddf688fca..0000000000 --- a/1-js/07-object-oriented-programming/01-property-descriptors/article.md +++ /dev/null @@ -1,320 +0,0 @@ - -# プロパティフラグとディスクリプタ - -ご存知の通り、オブジェクトはプロパティを格納することができます。 - -今まで、プロパティは単純な "key-value" ペアでしたが、実際にはオブジェクトプロパティはより複雑で調整可能なものです。 - -[cut] - -## プロパティフラグ - -オブジェクトプロパティは **`value`** に加えて、3つの特別な属性を持っています(いわゆる "フラグ" と呼ばれています)。 - -- **`writable`** -- `true` の場合は変更可能です。それ以外の場合は読み取り専用です。 -- **`enumerable`** -- `true` だとループで列挙されます。それ以外の場合は列挙されません。 -- **`configurable`** -- `true` の場合、プロパティを削除したり、これらの属性を変更することができます。 - -私たちは、まだこれらを見ていませんでした。なぜなら、一般的にはこれらは表示されないからです。"通常の方法" でプロパティを作成するとき、これらはすべて `true` です。が、いつでもそれを変更することができます。 - -まず、それらのフラグを取得する方法を見てみましょう。 - -メソッド [Object.getOwnPropertyDescriptor](mdn:js/Object/getOwnPropertyDescriptor) で、プロパティの *完全な* 情報を照会することができます。 - -構文は次の通りです: -```js -let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName); -``` - -`obj` -: 情報を取得するオブジェクトです。 - -`propertyName` -: プロパティ名です。 - -返却値はいわゆる "プロパティディスクリプタ" オブジェクトと呼ばれます。: それは値とすべてのフラグを含んでいます。 - -例: - -```js run -let user = { - name: "John" -}; - -let descriptor = Object.getOwnPropertyDescriptor(user, 'name'); - -alert( JSON.stringify(descriptor, null, 2 ) ); -/* プロパティディスクリプタ: -{ - "value": "John", - "writable": true, - "enumerable": true, - "configurable": true -} -*/ -``` - -[Object.defineProperty](mdn:js/Object/defineProperty) を使うことでフラグを変更することができます。 - -構文: - -```js -Object.defineProperty(obj, propertyName, descriptor) -``` - -`obj`, `propertyName` -: 処理するオブジェクトとプロパティです。 - -`descriptor` -: 適用するプロパティディスクリプタです。 - -もし、プロパティが存在する場合、`defineProperty` はそのフラグを更新します。そうでなければ、与えられた値とフラグでプロパティを作ります。その場合に、もしフラグが指定されていなければ `false` とみなされます。 - -例えば、ここではプロパティ `name` はすべて偽のフラグで作られます。: - -```js run -let user = {}; - -*!* -Object.defineProperty(user, "name", { - value: "John" -}); -*/!* - -let descriptor = Object.getOwnPropertyDescriptor(user, 'name'); - -alert( JSON.stringify(descriptor, null, 2 ) ); -/* -{ - "value": "John", -*!* - "writable": false, - "enumerable": false, - "configurable": false -*/!* -} - */ -``` - -上で "通常の方法で" 作成された `user.name` と比較してください: 今やすべてのフラグは false です。もしそのようにしたくなければ、`descriptor` で `true` をセットするのがよいでしょう。 - -では、例を使ってフラグの影響を見てみましょう。 - -## 読み取り専用(Read-only) - -`writable` フラグを変更して `user.name` を読み取り専用にしてみましょう: - -```js run -let user = { - name: "John" -}; - -Object.defineProperty(user, "name", { -*!* - writable: false -*/!* -}); - -*!* -user.name = "Pete"; // Error: Cannot assign to read only property 'name'... -*/!* -``` - -これで、`defineProperty` で上書きをしない限りは、誰も私たちの user.name を変えることはできません。 - -これは先程と同じ操作ですが、プロパティが存在しない場合です: - -```js run -let user = { }; - -Object.defineProperty(user, "name", { -*!* - value: "Pete", - // 新しいプロパティに対して、true のものは明示的に列挙する必要があります - enumerable: true, - configurable: true -*/!* -}); - -alert(user.name); // Pete -user.name = "Alice"; // Error -``` - - -## 列挙可能でない(Non-enumerable) - -今、カスタムの `toString` を `user` に追加しましょう。 - -通常、オブジェクトが持つ組み込みの `toString` は列挙可能ではありません。それは `for..in` では表示されません。しかし私たちが自身の `toString` を追加した場合、デフォルトではこのように `for..in` で表示されます。: - -```js run -let user = { - name: "John", - toString() { - return this.name; - } -}; - -// デフォルトでは、両方のプロパティは列挙されます: -for (let key in user) alert(key); // name, toString -``` - -もしもそれが好きじゃない場合には、`enumerable:false` をセットすることができます。そうすると、組み込みのものと同じように、`for..in` ループで表示されなくなります。: - -```js run -let user = { - name: "John", - toString() { - return this.name; - } -}; - -Object.defineProperty(user, "toString", { -*!* - enumerable: false -*/!* -}); - -*!* -// これで toString は消えました: -*/!* -for (let key in user) alert(key); // name -``` - -列挙可能でないプロパティは `Object.keys` からも除外されます。: - -```js -alert(Object.keys(user)); // name -``` - -## 変更できない(Non-configurable) - -組み込みオブジェクトやプロパティに対しては、変更不能フラグ(`configurable:false`)がプリセットされることがあります。 - -変更できないプロパティは `defineProperty` で削除したり変更することができません。 - -例えば、`Math.PI` は読み取り専用で、列挙不可であり、変更不能です。: - -```js run -let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI'); - -alert( JSON.stringify(descriptor, null, 2 ) ); -/* -{ - "value": 3.141592653589793, - "writable": false, - "enumerable": false, - "configurable": false -} -*/ -``` -したがって、プログラマーは `Math.PI` の値を変えることも上書きすることもできません。 - -```js run -Math.PI = 3; // Error - -// delete Math.PI もまた動作しません -``` - -変更不能なプロパティを作ることは一方通行です。それを戻すことはできません。なぜなら `defineProperty` は変更不能なプロパティでは動作しないためです。 - -ここでは、 `user.name` を "永遠に密封された" 定数にしています: - -```js run -let user = { }; - -Object.defineProperty(user, "name", { - value: "John", - writable: false, - configurable: false -}); - -*!* -// user.name またはそのフラグを変更することはできません -// これらすべて動作しません: -// user.name = "Pete" -// delete user.name -// defineProperty(user, "name", ...) -Object.defineProperty(user, "name", {writable: true}); // Error -*/!* -``` - -```smart header="use strict の場合にのみエラーとなります" -非 strict mode では、読み取り専用プロパティなどに書き込むときにエラーは発生しません。 しかし、操作は成功しません。フラグ違反の操作は、非 strict では無視されます。 -``` - -## Object.defineProperties - -一度に多くのプロパティが定義できるメソッド [Object.defineProperties(obj, descriptors)](mdn:js/Object/defineProperties)もあります。 - -構文は次の通りです: - -```js -Object.defineProperties(obj, { - prop1: descriptor1, - prop2: descriptor2 - // ... -}); -``` - -例えば: - -```js -Object.defineProperties(user, { - name: { value: "John", writable: false }, - surname: { value: "Smith", writable: false }, - // ... -}); -``` - -なので、一度に多くのプロパティをセットできます。 - -## Object.getOwnPropertyDescriptors - -一度にすべてのプロパティのディスクリプタを取得するには、[Object.getOwnPropertyDescriptors(obj)](mdn:js/Object/getOwnPropertyDescriptors) を使うことができます。 - -`Object.defineProperties` と合わせて、オブジェクトをクローンする "フラグを意識した" 方法として使うことができます。: - -```js -let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj)); -``` - -通常、私たちがオブジェクトをクローンするとき、次のようにプロパティをコピーするために代入を使います。: - -```js -for (let key in user) { - clone[key] = user[key] -} -``` - -...ですが、これはフラグはコピーしません。なので、"より良い" クローンを望むなら、 `Object.defineProperties` が優先されます。 - -もう1つの違いは、`for..in` はシンボルプロパティを無視しますが、`Object.getOwnPropertyDescriptors` はシンボリックなものを含む *すべての* プロパティディスクリプタを返します。 - -## グローバルにオブジェクトを隠す - -プロパティディスクリプタは個々のプロパティのレベルで動作します。 - -そこには、オブジェクト *全体* へのアクセスを制限するメソッドもあります。: - -[Object.preventExtensions(obj)](mdn:js/Object/preventExtensions) -: オブジェクトにプロパティを追加するのを禁止します。 - -[Object.seal(obj)](mdn:js/Object/seal) -: プロパティの追加、削除を禁止し、既存のすべてのプロパティに `configurable: false` をセットします。 - -[Object.freeze(obj)](mdn:js/Object/freeze) -: プロパティの追加、削除、変更を禁止し、既存のすべてのプロパティに `configurable: false, writable: false` をセットします。 - -また、それらを確認する方法もあります: - -[Object.isExtensible(obj)](mdn:js/Object/isExtensible) -: プロパティの追加が禁止されている場合に `false` を返します。それ以外は `true` です。 - -[Object.isSealed(obj)](mdn:js/Object/isSealed) -: プロパティの追加、削除が禁止されており、すべての既存のプロパティが `configurable: false` を持っている場合に `true` を返します。 - -[Object.isFrozen(obj)](mdn:js/Object/isFrozen) -: プロパティの追加、削除、変更が禁止されており、すべての現在のプロパティが `configurable: false, writable: false` の場合に `true` を返します。 - -これらのメソッドは実際にはめったに使われません。 diff --git a/1-js/07-object-oriented-programming/02-property-accessors/article.md b/1-js/07-object-oriented-programming/02-property-accessors/article.md deleted file mode 100644 index ccca0dc7f4..0000000000 --- a/1-js/07-object-oriented-programming/02-property-accessors/article.md +++ /dev/null @@ -1,240 +0,0 @@ - -# プロパティの getters と setters - -プロパティには2種類あります。 - -最初の種類は *データプロパティ* です。私たちはすでにそれがどうやって動作するのかを知っています。実際、これまで使ってきたすべてのプロパティはデータプロパティでした。 - -2つ目のプロパティの種類は新しいものです。それは *アクセサプロパティ* です。それらは基本的には値の取得やセットをする関数ですが、外部コードからは通常のプロパティのように見えます。 - -[cut] - -## Getters と setters - -アクセサプロパティは "getter" と "setter" メソッドで表現されます。オブジェクトリテラルでは、それらは `get` と `set` で表されますj.: - -```js -let obj = { - *!*get propName()*/!* { - // getter, obj.propName を取得するときにコードが実行されます - }, - - *!*set propName(value)*/!* { - // setter, obj.propName = value 時にコードが実行されます - } -}; -``` - -`obj.propName` が読まれたときに getter は動作し、setter は割り当てられたときです。 - -例えば、`name` と `surname` を持つ `user` オブジェクトを持っているとします。: - -```js run -let user = { - name: "John", - surname: "Smith" -}; -``` - -今、私たちは "John Smith" という値となる "fullName" プロパティを追加したいとします。もちろん、既存の情報のコピーペーストはしたくありません。ここで、アクセサとしてそれを実装することができます。: - -```js run -let user = { - name: "John", - surname: "Smith", - -*!* - get fullName() { - return `${this.name} ${this.surname}`; - } -*/!* -}; - -*!* -alert(user.fullName); // John Smith -*/!* -``` - -外部からは、アクセサプロパティは通常の変数に見えます。それがアクセサプロパティの考え方です。私たちは、関数として `user.fullName` を *呼び出すのではなく*、それを通常通り *読み込みます*。: getter は背後で実行されます。 - -今のところ、 `fullName` は getter しか持っていません。 `user.fullName =` を指定しようとすると、エラーが発生します。 - -`user.fullName` の setter を追加してそれを修正しましょう。: - -```js run -let user = { - name: "John", - surname: "Smith", - - get fullName() { - return `${this.name} ${this.surname}`; - }, - -*!* - set fullName(value) { - [this.name, this.surname] = value.split(" "); - } -*/!* -}; - -// set fullName は指定された値で実行されます -user.fullName = "Alice Cooper"; - -alert(user.name); // Alice -alert(user.surname); // Cooper -``` - -今 "仮想" プロパティを持っています。 読み書き可能ですが、実際には存在しません。 - -```smart header="アクセサプロパティは get/set でのみアクセス可能です" -プロパティは、 "データプロパティ" か "アクセサプロパティ" のいずれかになりますが、両方にはなりません。 - -プロパティが `get prop()` または `set prop()` で定義されると、それはアクセサプロパティです。 なので、getter で読まなければなりません。それに値を割り当てたいならば、setter を使わなければなりません。 - -setter または getter だけがある場合もあります。この場合は、プロパティの読み込みまたは書き込みはできません。 - -``` - - -## アクセサディスクリプタ - -アクセサプロパティのためのディスクリプタは、データプロパティと比べて異なります。 - -アクセサプロパティでは、`value` と `writable` がありませんが、代わりに、`get` と `set` 関数があります。 - -したがって、アクセサディスクリプタには次のものがあります: - -- **`get`** -- 引数なしの関数で、プロパティが読まれたときに動作します。 -- **`set`** -- 1つの引数をもつ巻数で、プロパティがセットされたときに呼ばれます。 -- **`enumerable`** -- データプロパティと同じです。 -- **`configurable`** -- データプロパティと同じです。 - -例えば、アクセサ `fullName` を `defineProperty` で作るとき、`get` と `set` をディスクリプタに渡すことができます。: - -```js run -let user = { - name: "John", - surname: "Smith" -}; - -*!* -Object.defineProperty(user, 'fullName', { - get() { - return `${this.name} ${this.surname}`; - }, - - set(value) { - [this.name, this.surname] = value.split(" "); - } -*/!* -}); - -alert(user.fullName); // John Smith - -for(let key in user) alert(key); -``` - -プロパティはアクセサかデータプロパティのいずれかになれますが、両方に離れないことに再度注意してください。 - -もしも `get` と `value` を同じディスクリプタで指定しようとすると、エラーになります。: - -```js run -*!* -// Error: Invalid property descriptor. -*/!* -Object.defineProperty({}, 'prop', { - get() { - return 1 - }, - - value: 2 -}); -``` - -## スマートな getters/setters - -Getter/setter は、実際のプロパティ値をラッパーとして使用して、より多くのコントロールを得ることができます。 - -例えば、`user` で短すぎる名前を禁止したい場合、`name` を特別なプロパティ `_name` に格納することができます。そして、setter で割り当てをフィルタします。: - -```js run -let user = { - get name() { - return this._name; - }, - - set name(value) { - if (value.length < 4) { - alert("Name is too short, need at least 4 characters"); - return; - } - this._name = value; - } -}; - -user.name = "Pete"; -alert(user.name); // Pete - -user.name = ""; // Name is too short... -``` - -技術的には、外部コードは `user._name` を使うことで、直接 name にアクセスできるかもしれません。しかし、アンダースコア `"_"` で始まるプロパティは内部のもので、外部のオブジェクトから触るべきではないということは広く知られています。 - - -## 互換性のために使用する - -getter と setter の裏にある素晴らしいアイデアの1つは -- それらは "通常の" データプロパティを制御し、それをいつでも調整することができます。 - -例えば、データプロパティ `name` と `age` を使って user オブジェクトを実装し始めました。: - -```js -function User(name, age) { - this.name = name; - this.age = age; -} - -let john = new User("John", 25); - -alert( john.age ); // 25 -``` - -...しかし、遅かれ早かれ、それを変更するかもしれません。より正確で便利のため、`age` の代わりに `birthday` を格納することに決めるかもしれません。: - -```js -function User(name, birthday) { - this.name = name; - this.birthday = birthday; -} - -let john = new User("John", new Date(1992, 6, 1)); -``` - -さて、まだ `age` プロパティを使っている古いコードはどうすればよいでしょうか? - -そのような箇所をすべて見つけて直していくこともできますが、時間がかかったり別の人が書いているコードであれば直すのが難しいかもしれません。その上、`age` は `user` が持っていても良いものですよね?場所によってはそれを必要とするかもしれません。 - -`age` の getter を追加すると、問題が緩和されます: - -```js run no-beautify -function User(name, birthday) { - this.name = name; - this.birthday = birthday; - -*!* - // age は現在の日付と誕生日から計算されます - Object.defineProperty(this, "age", { - get() { - let todayYear = new Date().getFullYear(); - return todayYear - this.birthday.getFullYear(); - } - }); -*/!* -} - -let john = new User("John", new Date(1992, 6, 1)); - -alert( john.birthday ); // birthday は利用可能です -alert( john.age ); // ...age も同様です -``` - -これで古いコードも機能します。 diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/task.md b/1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/task.md deleted file mode 100644 index b6291aef30..0000000000 --- a/1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/task.md +++ /dev/null @@ -1,35 +0,0 @@ -importance: 5 - ---- - -# なぜ2匹のハムスターがお腹一杯? - -私たちは2匹のハムスターを持っています: `speedy` と `lazy` は一般的な `hamster` オブジェクトを継承しています。 - -そのうちの1匹に餌をやるとき、もう1匹もお腹一杯になります。なぜでしょう?どのような直しますか? - -```js run -let hamster = { - stomach: [], - - eat(food) { - this.stomach.push(food); - } -}; - -let speedy = { - __proto__: hamster -}; - -let lazy = { - __proto__: hamster -}; - -// 一方が食べ物を見つけました -speedy.eat("apple"); -alert( speedy.stomach ); // apple - -// もう一方も持っています。なぜでしょう?直してください。 -alert( lazy.stomach ); // apple -``` - diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/article.md b/1-js/07-object-oriented-programming/03-prototype-inheritance/article.md deleted file mode 100644 index 6cbc549344..0000000000 --- a/1-js/07-object-oriented-programming/03-prototype-inheritance/article.md +++ /dev/null @@ -1,254 +0,0 @@ -# プロトタイプ継承 - -プログラミングでは、何かを取ってそれを拡張することがしばしばあります。 - -例えば、プロパティとメソッドをもつ `user` オブジェクトを持っているとします。そして、そのいくつかを僅かに変更した `admin` や `guest` を作りたいとします。コピーや再実装ではなく、単にその上に新しいオブジェクトを作成することで、`user` が持っているものを再利用したいです。 - -*プロトタイプ継承* はそれを助ける言語の機能です。 - -[cut] - -## プロトタイプ [[Prototype]] - -JavaScriptでは、オブジェクトは特別な隠しプロパティ `[[Prototype]]` を持っており、それは `null` または別のオブジェクトを参照します。そのオブジェクトは "プロトタイプ" と呼ばれます。 - -![prototype](object-prototype-empty.png) - -`[[Prototype]]` は "魔法のような" 意味を持っています。私たちが `object` からプロパティを読みたいときで、それがない場合、JavaScriptは自動的に、プロトタイプからそれを取得します。プログラミングではこのようなことを "プロトタイプ継承" と呼びます。多くのクールな言語機能やプログラミングテクニックは、これがベースになっています。 - -プロパティ `[[Prototype]]` は内部であり隠されていますが、セットする多くの方法があります。 - -それらの1つは、次のように `__proto__` を使う方法です: - -```js run -let animal = { - eats: true -}; -let rabbit = { - jumps: true -}; - -*!* -rabbit.__proto__ = animal; -*/!* -``` - -`__proto__` は `[[Prototype]]` と *同一ではない* ことに注意してください。これはそのための getter/setter です。あとでセットする別の方法について話しますが、今のところ `__proto__` の理解はそれで問題ありません。 - -もしも `rabbit` の中のプロパティを探し、それがない場合、JavaScriptは自動で `animal` からそれを取ります。 - -例: - -```js run -let animal = { - eats: true -}; -let rabbit = { - jumps: true -}; - -*!* -rabbit.__proto__ = animal; // (*) -*/!* - -// 今、rabbit で両方のプロパティを見つけることができます: -*!* -alert( rabbit.eats ); // true (**) -*/!* -alert( rabbit.jumps ); // true -``` - -ここで、行 `(*)` は `rabbit` のプロトタイプに `animal` をセットしています。 - -次に、`alert` がプロパティ `rabbit.eats` `(**)` を読もうとしたとき、それは `rabbit` にはないので、JavaScriptは `[[Prototype]]` 参照に従って、`animal` の中でそれを見つけます(下から上に向かいます)。 - -![](proto-animal-rabbit.png) - -ここでは、私たちは "`animal` は `rabbit` のプロトタイプ" または "`rabbit` がプロトタイプ的に `animal` を継承している" という事ができます。" - -したがって、もし `animal` が多くの役立つプロパティやメソッドを持っている場合、それらは自動的に `rabbit` でも利用可能になります。 -このようなプロパティは "継承" と呼ばれます。 - -もし `animal` がメソッドを持っている場合、`rabbit` でもそれを呼ぶことができます: - -```js run -let animal = { - eats: true, -*!* - walk() { - alert("Animal walk"); - } -*/!* -}; - -let rabbit = { - jumps: true, - __proto__: animal -}; - -// walk は prototype から取られました -*!* -rabbit.walk(); // Animal walk -*/!* -``` - -メソッドは次のように自動的にプロトタイプから取られます。: - -![](proto-animal-rabbit-walk.png) - -プロトタイプチェーンは長くても問題ありません。: - - -```js run -let animal = { - eats: true, - walk() { - alert("Animal walk"); - } -}; - -let rabbit = { - jumps: true, - __proto__: animal -}; - -let longEar = { - earLength: 10, - __proto__: rabbit -} - -// walk は prototype チェーンから取られました -longEar.walk(); // Animal walk -alert(longEar.jumps); // true (rabbit から) -``` - -![](proto-animal-rabbit-chain.png) - -実際には、2つの制限があります。: - -1. 参照を循環させることはできません。JavaScriptは、循環するように `__proto__` を割り当てようとするとエラーを投げます。 -2. `__proto__` の値はオブジェクトまたは `null` になります。プリミティブのような、それ以外のすべての値は無視されます。 - -また、それは明らかかもしれませんが、1つの `[[Prototype]]` しか存在しません。 オブジェクトは2つの他のものから継承することはできません。 - -## 読み書きのルール - -プロトタイプは、プロパティを読むためだけに使われます。 - -データプロパティ(getter/setter ではない)の場合、書き込み/削除操作はオブジェクトで直接動作します。 - -下の例では、自身の `walk` メソッドを `rabbit` に割り当てています。: - -```js run -let animal = { - eats: true, - walk() { - /* このメソッドは rabbit では使われません */ - } -}; - -let rabbit = { - __proto__: animal -} - -*!* -rabbit.walk = function() { - alert("Rabbit! Bounce-bounce!"); -}; -*/!* - -rabbit.walk(); // Rabbit! Bounce-bounce! -``` - -これ以降、`rabbit.walk()` 呼び出しは、プロトタイプを使うことなく、オブジェクトの中にすぐにメソッドを見つけ、それを実行します。 - -![](proto-animal-rabbit-walk-2.png) - -getter/setter の場合 -- もしプロパティの読み書きをすると、プロトタイプで参照されて呼び出されます。 - -例えば、以下のコードで `admin.fullName` プロパティをチェックしてください: - -```js run -let user = { - name: "John", - surname: "Smith", - - set fullName(value) { - [this.name, this.surname] = value.split(" "); - }, - - get fullName() { - return `${this.name} ${this.surname}`; - } -}; - -let admin = { - __proto__: user, - isAdmin: true -}; - -alert(admin.fullName); // John Smith (*) - -// setter がトリガします! -admin.fullName = "Alice Cooper"; // (**) -``` - -ここで、行 `(*)` では、プロパティ `admin.fullName` はプロトタイプ `user` が getter を持っているので、それが呼ばれます。また、行 `(**)` では、プロパティはプロトタイプに setter を持っているので、それが呼ばれます。 - -## "this" の値 - -上の例で、興味深い質問が起きるかもしれません。: `set fullName(value)` の内側での `this` の値はなんでしょうか? -プロパティ `this.name` と `this.surname` が書かれているのはどこでしょうか? `user` または `admin` ? - -答えはシンプルです: `this` はプロトタイプによる影響を持ったく受けません。 - -**メソッドがどこにあるかは関係ありません:オブジェクトの中でも、そのプロトタイプ内でも。メソッド呼び出しでは、`this` は常にドットの前のオブジェクトです。** - -したがって、setter は実際に `this` として `admin` を使い、`user` ではありません。 - -それは、実際には非常に重要なことです。なぜなら、多くのメソッドを持つ大きなオブジェクトを持ち、それを継承する可能性があるからです。次に、継承されたオブジェクトの上でそれらのメソッドを実行し、大きなオブジェクトではなく、継承したオブジェクトの状態を変更します。 - -例えば、ここでは `animal` は "メソッド格納域" を表現しており、`rabbit` はそれを使います。 - -呼び出し `rabbit.sleep()` は `rabbit` オブジェクトに `this.isSleeping` をセットします。: - -```js run -// animal がメソッドを持っています -let animal = { - walk() { - if (!this.isSleeping) { - alert(`I walk`); - } - }, - sleep() { - this.isSleeping = true; - } -}; - -let rabbit = { - name: "White Rabbit", - __proto__: animal -}; - -// rabbit.isSleeping を変更する -rabbit.sleep(); - -alert(rabbit.isSleeping); // true -alert(animal.isSleeping); // undefined (prototype にそのようなプロパティはありません) -``` - -結果の図は次のようになります: - -![](proto-animal-rabbit-walk-3.png) - -もしも私たちが `bird`, `snake` など `animal` から継承された他のオブジェクトを持っていた場合、それらもまた `animal` のメソッドへのアクセスを得ます。しかし各メソッドでの `this` は対応するオブジェクトであり、`animal` ではなく、呼び出し時に(前のドット)で評価されます。 だから私たちが `this` にデータを書き込むとき、それはこれらのオブジェクトに格納されます。 - -結果として、メソッドは共有されますが、オブジェクトの状態は共有されません。 - -## サマリ - -- JavaScriptでは、すべてのオブジェクトは隠れた `[[Prototype]]` プロパティを持っており、それは別のオブジェクトまたは `null` です。 -- それにアクセスするために `obj.__proto__` を使うことができます(他の方法もあります。それらは後ほど学びます)。 -- `[[Prototype]]` によるオブジェクトの参照は "プロトタイプ" と呼ばれます。 -- もしも `obj` のプロパティを読みたい、またはメソッドを呼び出したいが存在しない場合、JavaScriptはそれをプロトタイプの中で見つけようとします。書き込み/削除操作はオブジェクトに対して直接動作し、プロトタイプを使いません(プロパティが setter でない限り)。 -- もしも `obj.method()` を呼び出し、`method` がプロトタイプから取られた場合も、`this` は依然として `obj` を参照します。したがって、メソッドはたとえ継承されていたとしても、常に現在のオブジェクトで動作します。 diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/object-prototype-empty.png b/1-js/07-object-oriented-programming/03-prototype-inheritance/object-prototype-empty.png deleted file mode 100644 index 1d228b5c8a..0000000000 Binary files a/1-js/07-object-oriented-programming/03-prototype-inheritance/object-prototype-empty.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/object-prototype-empty@2x.png b/1-js/07-object-oriented-programming/03-prototype-inheritance/object-prototype-empty@2x.png deleted file mode 100644 index a4c195ad69..0000000000 Binary files a/1-js/07-object-oriented-programming/03-prototype-inheritance/object-prototype-empty@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-chain.png b/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-chain.png deleted file mode 100644 index a302343e2d..0000000000 Binary files a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-chain.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-chain@2x.png b/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-chain@2x.png deleted file mode 100644 index 4d3311998e..0000000000 Binary files a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-chain@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-2.png b/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-2.png deleted file mode 100644 index 19c1881a2f..0000000000 Binary files a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-2.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-2@2x.png b/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-2@2x.png deleted file mode 100644 index cd38624ab0..0000000000 Binary files a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-2@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-3.png b/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-3.png deleted file mode 100644 index 0ea869d3c5..0000000000 Binary files a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-3.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-3@2x.png b/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-3@2x.png deleted file mode 100644 index 194db77edd..0000000000 Binary files a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk-3@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk.png b/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk.png deleted file mode 100644 index 5bd2111474..0000000000 Binary files a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk@2x.png b/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk@2x.png deleted file mode 100644 index 1e4ef6efbd..0000000000 Binary files a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit-walk@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit.png b/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit.png deleted file mode 100644 index c18e020525..0000000000 Binary files a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit@2x.png b/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit@2x.png deleted file mode 100644 index 4d9e8ec094..0000000000 Binary files a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-animal-rabbit@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-user-admin.png b/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-user-admin.png deleted file mode 100644 index aed69c59e0..0000000000 Binary files a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-user-admin.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-user-admin@2x.png b/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-user-admin@2x.png deleted file mode 100644 index 2b11b2568b..0000000000 Binary files a/1-js/07-object-oriented-programming/03-prototype-inheritance/proto-user-admin@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/article.md b/1-js/07-object-oriented-programming/04-function-prototype/article.md deleted file mode 100644 index 87c1dc4959..0000000000 --- a/1-js/07-object-oriented-programming/04-function-prototype/article.md +++ /dev/null @@ -1,176 +0,0 @@ -# F.prototype - -最新のJavaScriptでは、前の記事で説明したとおり、`__proto__` を使ってプロトタイプをセットすることができます。しかし、それはいつでもそうではありませんでした。 - -[cut] - -JavaScriptは最初からプロトタイプの継承を持っています。 それは言語の中心的な特徴の1つでした。 - -しかし、以前は別(であり唯一)の方法がありました。: コンストラクタ関数の `"prototype"` プロパティを使うことです。そして、それを使っているスクリプトはまだたくさんあります。 - -## "prototype" プロパティ - -すでにご存知の通り、`new F()` は新しいオブジェクトを作ります。 - -`new F()` で新しいオブジェクトが作られるとき、オブジェクトの `[[Prototype]]` は `F.prototype` にセットされます。 - -言い換えると、もし `F` がオブジェクト型の値をもつ `prototype` プロパティを持っている場合、`new` 演算子は新しいオブジェクトに対してそれを `[[Prototype]]` にセットします。 - -ここで `F.prototype` は `F` 上の `"prototype"` と名付けられた通常のプロパティを意味していることに注意してください。用語 "プロトタイプ" と似ていますが、ここでは本当にその名前をもつ通常のプロパティを意味しています。 - - -ここではその例です: - -```js run -let animal = { - eats: true -}; - -function Rabbit(name) { - this.name = name; -} - -*!* -Rabbit.prototype = animal; -*/!* - -let rabbit = new Rabbit("White Rabbit"); // rabbit.__proto__ == animal - -alert( rabbit.eats ); // true -``` - -`Rabbit.prototype = animal` の設定は、文字通り次のことを述べています。: "`new Rabbit` が生成される時、その `[[Prototype]]` へ `animal` を割り当てます。" - -これが結果のイメージです: - -![](proto-constructor-animal-rabbit.png) - -イメージ上で、`"prototype"` は水平矢印で、それは通常のプロパティです。`[[Prototype]]` は縦矢印で、`animal` から `rabbit` の継承を意味しています。 - - -## デフォルトの F.prototype, constructor プロパティ - -すべての関数は、たとえ明示的に提供されていなくても `"prototype"` プロパティを持っています。 - -デフォルトの `"prototype"` は `constructor` というプロパティだけを持つオブジェクトで、それは関数自体を指します。 - -こんな感じです: - -```js -function Rabbit() {} - -/* デフォルト prototype -Rabbit.prototype = { constructor: Rabbit }; -*/ -``` - -![](function-prototype-constructor.png) - -コードでそれを確認できます: - -```js run -function Rabbit() {} -// デフォルトでは: -// Rabbit.prototype = { constructor: Rabbit } - -alert( Rabbit.prototype.constructor == Rabbit ); // true -``` - -当然、何もしない場合、 `constructor` プロパティは `[[Prototype]]` を通じてすべての rabbit が利用できます。: - -```js run -function Rabbit() {} -// デフォルトでは: -// Rabbit.prototype = { constructor: Rabbit } - -let rabbit = new Rabbit(); // {constructor: Rabbit} の継承 - -alert(rabbit.constructor == Rabbit); // true (prototype から) -``` - -![](rabbit-prototype-constructor.png) - -`constructor` プロパティを使って既存のものと同じコンストラクタを使って新しいオブジェクトを作成することができます。 - -このように: - -```js run -function Rabbit(name) { - this.name = name; - alert(name); -} - -let rabbit = new Rabbit("White Rabbit"); - -*!* -let rabbit2 = new rabbit.constructor("Black Rabbit"); -*/!* -``` - -これは、オブジェクトを持っているが、どのコンストラクタが使われたか分からない場合(例えば3rdパーティライブラリが使われているなど)で、同じ種類のものを使って別のオブジェクトを作る必要がある場合に便利です。 - -しかし、おそらく `"constructor"` に関する最も重要なことは... - -**...JavaScript 自体は正しい `"constructor"` 値を保証しません。** - -はい、関数のためのデフォルトの `"prototype"` は存在しますが、それがすべてです。その後どうなるかは私たち次第です。 - -特に、もしデフォルトプロトタイプ全体を置き換えると、その中に `"constructor"` はなくなります。 - -例: - -```js run -function Rabbit() {} -Rabbit.prototype = { - jumps: true -}; - -let rabbit = new Rabbit(); -*!* -alert(rabbit.constructor === Rabbit); // false -*/!* -``` - -したがって、正しい `"constructor"` を維持するためには、全体を上書きする代わりに、デフォルト `"prototype"` に対して追加/削除を行います。: - -```js -function Rabbit() {} - -// 完全に Rabbit.prototype を上書きはしません -// 単に追加するだけです -Rabbit.prototype.jumps = true -// デフォルト Rabbit.prototype.constructor は保持されます -``` - -もしくは、代替として手動で `constructor` プロパティを再び作ります。: - -```js -Rabbit.prototype = { - jumps: true, -*!* - constructor: Rabbit -*/!* -}; - -// 追加したので、これで constructor も正しいです -``` - -## サマリ - -このチャプターでは、constructor 関数を通して作成されたオブジェクトのための `[[Prototype]]` を設定方法について簡単に説明しました。後で、それに依存するより高度なプログラミングパターンを見ていきます。 - -すべてが非常にシンプルで、物事を明確にするための留意事項はほんの少しです。: - -- `F.prototype` プロパティは `[[Prototype]]` と同じではありません。`F.prototype` がする唯一のことは: `new F()` が呼ばれたときに新しいオブジェクトの `[[Prototype]]` をセットすることです。 -- `F.prototype` の値はオブジェクトまたは null でなければなりません。: 他の値では動作しません。 -- `"prototype"` プロパティはコンストラクタ関数に設定され、`new` で呼び出されたときにのみ、特別な効果があります。 - -通常のオブジェクトでは、`prototype` は特別なものではありません。: -```js -let user = { - name: "John", - prototype: "Bla-bla" // no magic at all -}; -``` - -デフォルトでは、すべての関数は `F.prototype = { constructor: F }` を持っているので、その `"constructor"` プロパティへアクセスすることで、オブジェクトの constructor を取得することができます。 diff --git a/1-js/07-object-oriented-programming/04-function-prototype/function-prototype-constructor.png b/1-js/07-object-oriented-programming/04-function-prototype/function-prototype-constructor.png deleted file mode 100644 index 0dbc7b8fc4..0000000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/function-prototype-constructor.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/function-prototype-constructor@2x.png b/1-js/07-object-oriented-programming/04-function-prototype/function-prototype-constructor@2x.png deleted file mode 100644 index e38cb85bd9..0000000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/function-prototype-constructor@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-array-tostring.png b/1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-array-tostring.png deleted file mode 100644 index 83258d0614..0000000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-array-tostring.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-array-tostring@2x.png b/1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-array-tostring@2x.png deleted file mode 100644 index d77cc8f4ec..0000000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-array-tostring@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-classes.png b/1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-classes.png deleted file mode 100644 index fa6a2943f6..0000000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-classes.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-classes@2x.png b/1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-classes@2x.png deleted file mode 100644 index 9368d7e502..0000000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/native-prototypes-classes@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/object-prototype-1.png b/1-js/07-object-oriented-programming/04-function-prototype/object-prototype-1.png deleted file mode 100644 index e7c6bdb212..0000000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/object-prototype-1.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/object-prototype-1@2x.png b/1-js/07-object-oriented-programming/04-function-prototype/object-prototype-1@2x.png deleted file mode 100644 index 34c256bbfd..0000000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/object-prototype-1@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/object-prototype.png b/1-js/07-object-oriented-programming/04-function-prototype/object-prototype.png deleted file mode 100644 index d97d87d2c3..0000000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/object-prototype.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/object-prototype@2x.png b/1-js/07-object-oriented-programming/04-function-prototype/object-prototype@2x.png deleted file mode 100644 index 76ce4c9ac5..0000000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/object-prototype@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/proto-constructor-animal-rabbit.png b/1-js/07-object-oriented-programming/04-function-prototype/proto-constructor-animal-rabbit.png deleted file mode 100644 index 3eec740fbb..0000000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/proto-constructor-animal-rabbit.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/proto-constructor-animal-rabbit@2x.png b/1-js/07-object-oriented-programming/04-function-prototype/proto-constructor-animal-rabbit@2x.png deleted file mode 100644 index ed28a388a0..0000000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/proto-constructor-animal-rabbit@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/rabbit-animal-object.png b/1-js/07-object-oriented-programming/04-function-prototype/rabbit-animal-object.png deleted file mode 100644 index 3254270f38..0000000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/rabbit-animal-object.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/rabbit-animal-object@2x.png b/1-js/07-object-oriented-programming/04-function-prototype/rabbit-animal-object@2x.png deleted file mode 100644 index f794d7b84a..0000000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/rabbit-animal-object@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/rabbit-prototype-constructor.png b/1-js/07-object-oriented-programming/04-function-prototype/rabbit-prototype-constructor.png deleted file mode 100644 index d3ef344852..0000000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/rabbit-prototype-constructor.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/04-function-prototype/rabbit-prototype-constructor@2x.png b/1-js/07-object-oriented-programming/04-function-prototype/rabbit-prototype-constructor@2x.png deleted file mode 100644 index 45cbb6baab..0000000000 Binary files a/1-js/07-object-oriented-programming/04-function-prototype/rabbit-prototype-constructor@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/article.md b/1-js/07-object-oriented-programming/05-native-prototypes/article.md deleted file mode 100644 index 42fa808aa0..0000000000 --- a/1-js/07-object-oriented-programming/05-native-prototypes/article.md +++ /dev/null @@ -1,179 +0,0 @@ -# ネイティブのプロトタイプ - -`"prototype"` プロパティはJavaScript自身のコア部分で広く使われています。すべての組み込みのコンストラクタ関数はそれを使っています。 - -最初に単純なオブジェクトの場合を、次により複雑なオブジェクトの場合にどのようになるかを見ていきましょう。 - -## Object.prototype - -空のオブジェクトを出力してみましょう。: - -```js run -let obj = {}; -alert( obj ); // "[object Object]" ? -``` - -文字列 `"[object Object]"` を生成するコードはどこにあるのでしょう? それは組み込みの `toString` メソッドですが、どこにあるのでしょう? `obj` は空です! - -...しかし、短い記法 `obj = {}` は `obj = new Object()` と同じで、その `Object` は -- 組み込みのオブジェクトコンストラクタ関数です。その関数は `toString` や他の関数を持つ巨大なオブジェクトを参照する `Object.prototype` を持っています。 - -このようになります(すべて組み込みです): - -![](object-prototype.png) - -`new Object()` が呼ばれた(もしくはリテラルオブジェクト `{...}` が作られた)とき、その `[[Prototype]]` は前のチャプターで私たちが話してきたルールによって、 `Object.prototype` にセットされます。: - -![](object-prototype-1.png) - -その後、`obj.toString()` が呼ばれると -- `Object.prototype` からメソッドが取り出されます。 - -このようにして、それを確認することができます: - -```js run -let obj = {}; - -alert(obj.__proto__ === Object.prototype); // true -// obj.toString === obj.__proto__.toString == Object.prototype.toString -``` - -上の `Object.prototype` のチェーンで、追加の `[[Prototype]]` がないことに注意してください。: - -```js run -alert(Object.prototype.__proto__); // null -``` - -## 他の組み込みのプロトタイプ - -`Array`, `Date`, `Function` のような、他の組み込みのプロトタイプもまたプロトタイプにメソッドを保持しています。 - -例えば、配列 `[1, 2, 3]` を作るとき、デフォルトの `new Array()` コンストラクタが内部で使われます。なので、配列データは新しいオブジェクトに書き込まれ、`Array.prototype` はそのプロトタイプとなり、メソッドを提供します。これは非常にメモリ効率が良いです。 - -仕様では、すべての組み込みのプロトタイプは先頭に `Object.prototype` を持っています。なので、"すべてはオブジェクトを継承している" という人もいます。 - -全体図は次のとおりです(3つの組み込みについて書いています): - -![](native-prototypes-classes.png) - -プロトタイプを手動でチェックしてみましょう。: - -```js run -let arr = [1, 2, 3]; - -// Array.prototype から継承している? -alert( arr.__proto__ === Array.prototype ); // true - -// 次に Object.prototype からは継承している? -alert( arr.__proto__.__proto__ === Object.prototype ); // true - -// そしてトップの null -alert( arr.__proto__.__proto__.__proto__ ); // null -``` - -プロトタイプのメソッドのいくつかは重複する可能性があります。例えば、`Array.prototype` はカンマ区切りで要素を表示する自身の `toString` を持っています。: - -```js run -let arr = [1, 2, 3] -alert(arr); // 1,2,3 <-- Array.prototype.toString の結果 -``` - -以前見たように、`Object.prototype` も同様に `toString` を持っていますが、`Array.prototype` はチェーンでより近いので、配列のバリアントが使われます。 - -![](native-prototypes-array-tostring.png) - - -Chrome developer console のようなブラウザ内のツールでも継承を表示できます(組み込みオブジェクトのために `console.fir` を使う必要があるかもしれません)。 - -![](console_dir_array.png) - -他の組み込みオブジェクトも同じように動作します。関数でさえも。それらは組み込みの `Function` コンストラクタのオブジェクトであり、メソッドです: `call/apply` など、`Function.prototype` から取り出されたものです。関数には独自の `toString`もあります。 - -```js run -function f() {} - -alert(f.__proto__ == Function.prototype); // true -alert(f.__proto__.__proto__ == Object.prototype); // true, object からの継承 -``` - -## プリミティブ(Primitives) - -最も複雑なことは、文字列、数値、ブール値で起こります。 - -覚えている通り、それらはオブジェクトではありません。しかし、それらのプロパティへアクセスをしようとした場合、組み込みのコンストラクタ `String`, `Number`, `Boolean` を使った一時的なラッパーオブジェクトが作られます。それらはメソッドを提供し、消えます。 - -それらのオブジェクトは我々には見えない形で作られ、ほとんどのエンジンはそれらを最適化しますが、仕様ではこのように正確に説明されています。それらのオブジェクトのメソッドもまた `String.prototype`, `Number.prototype` や `Boolean.prototype` として利用可能なものとしてプロトタイプに存在します。 - -```warn header="値 `null` と `undefined` はオブジェクトラッパーを持っていません" -特別な値 `null` や `undefined` は別です。それらはオブジェクトラッパーを持ちません。そのため、利用可能なメソッドやプロパティはありません。また、それらに対応するプロトタイプもありません。 -``` - -## ネイティブプロトタイプの変更 - -ネイティブプロトタイプは変更することができます。例えば、もしあるメソッドを `String.prototype` に追加した場合、それはすべての文字列で利用可能になります。: - -```js run -String.prototype.show = function() { - alert(this); -}; - -"BOOM!".show(); // BOOM! -``` - -開発の過程で、私たちは新しい組み込みメソッドを持っていたいと考えているかもしれません。そして、それをネイティブプロトタイプに加えたいという、若干の誘惑があるかもしれません。 しかし、それは一般的には悪い考えです。 - -プロトタイプはグローバルです。なので、コンフリクトを起こしやすいです。もし2つのライブラリがメソッド `String.prototype.show` を追加している場合、片方はもう一方に上書きされます。 - -現代のプログラミングでは、ネイティブプロトタイプの変更が認められたケースが1つだけあります。それはポリフィルです。言い換えると、もし我々のJavaScriptエンジン(または我々がサポートしたいもの)がまだサポートしていないJavaScript仕様上のメソッドがある場合、それを手動で実装し、組み込みのプロトタイプにそれを取り込むことができます。 - -例: - -```js run -if (!String.prototype.repeat) { // もしこのようなメソッドがない場合 - // prototype に追加します - - String.prototype.repeat = function(n) { - // 文字列を n 回繰り返す - - // 実際、このコードはこれより複雑になります - // "n" の負の値に対するエラーのスロー - // 完全なアルゴリズムは仕様にあります - return new Array(n + 1).join(this); - }; -} - -alert( "La".repeat(3) ); // LaLaLa -``` - -## プロトタイプからの借用 - -チャプター で私たちはメソッドの借用について話しました。: - -```js run -function showArgs() { -*!* - // 配列から join を借り、引数コンテキストでそれを呼び出す - alert( [].join.call(arguments, " - ") ); -*/!* -} - -showArgs("John", "Pete", "Alice"); // John - Pete - Alice -``` - -`join` は `Array.prototype` にあるため、そこから直接呼び出すことができ、次のように書き直すことができます: - -```js -function showArgs() { -*!* - alert( Array.prototype.join.call(arguments, " - ") ); -*/!* -} -``` - -これは、余分な配列オブジェクト `[]` の作成を避けることができるので、より効率的です。一方で記述は長いですが。 - -## サマリ - -- すべての組み込みオブジェクトは同じパターンに従います。: - - メソッドはプロトタイプに保持されています(`Array.prototype`, `Object.prototype`, `Date.prototype` など)。 - - オブジェクト自身はデータのみを保持します(配列アイテム、オブジェクトプロパティ、日付)。 -- プリミティブもまたラッパーオブジェクトのプロトタイプにメソッドを保持します。: `Number.prototype`, `String.prototype`, `Boolean.prototype` 。`undefined` と `null` にだけはラッパーオブジェクトはありません。 -- 組み込みのプロトタイプを変更したり、新しいメソッドを実装することができます。 しかし、それを変更することはお勧めしません。 おそらく唯一許可されるケースは、新しい標準を追加したがまだエンジンのJavaScriptメソッドではサポートされていないときだけです。 diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/function-prototype-constructor.png b/1-js/07-object-oriented-programming/05-native-prototypes/function-prototype-constructor.png deleted file mode 100644 index 0dbc7b8fc4..0000000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/function-prototype-constructor.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/function-prototype-constructor@2x.png b/1-js/07-object-oriented-programming/05-native-prototypes/function-prototype-constructor@2x.png deleted file mode 100644 index e38cb85bd9..0000000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/function-prototype-constructor@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-array-tostring.png b/1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-array-tostring.png deleted file mode 100644 index 83258d0614..0000000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-array-tostring.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-array-tostring@2x.png b/1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-array-tostring@2x.png deleted file mode 100644 index d77cc8f4ec..0000000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-array-tostring@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-classes.png b/1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-classes.png deleted file mode 100644 index fa6a2943f6..0000000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-classes.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-classes@2x.png b/1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-classes@2x.png deleted file mode 100644 index 9368d7e502..0000000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/native-prototypes-classes@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-1.png b/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-1.png deleted file mode 100644 index e7c6bdb212..0000000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-1.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-1@2x.png b/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-1@2x.png deleted file mode 100644 index 34c256bbfd..0000000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-1@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-null.png b/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-null.png deleted file mode 100644 index 7c2e3f9c78..0000000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-null.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-null@2x.png b/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-null@2x.png deleted file mode 100644 index fec1facb0b..0000000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype-null@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype.png b/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype.png deleted file mode 100644 index d97d87d2c3..0000000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype@2x.png b/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype@2x.png deleted file mode 100644 index 76ce4c9ac5..0000000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/object-prototype@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/proto-constructor-animal-rabbit.png b/1-js/07-object-oriented-programming/05-native-prototypes/proto-constructor-animal-rabbit.png deleted file mode 100644 index 3eec740fbb..0000000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/proto-constructor-animal-rabbit.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/proto-constructor-animal-rabbit@2x.png b/1-js/07-object-oriented-programming/05-native-prototypes/proto-constructor-animal-rabbit@2x.png deleted file mode 100644 index ed28a388a0..0000000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/proto-constructor-animal-rabbit@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/rabbit-prototype-constructor.png b/1-js/07-object-oriented-programming/05-native-prototypes/rabbit-prototype-constructor.png deleted file mode 100644 index d3ef344852..0000000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/rabbit-prototype-constructor.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/rabbit-prototype-constructor@2x.png b/1-js/07-object-oriented-programming/05-native-prototypes/rabbit-prototype-constructor@2x.png deleted file mode 100644 index 45cbb6baab..0000000000 Binary files a/1-js/07-object-oriented-programming/05-native-prototypes/rabbit-prototype-constructor@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/article.md b/1-js/07-object-oriented-programming/06-prototype-methods/article.md deleted file mode 100644 index a70843e777..0000000000 --- a/1-js/07-object-oriented-programming/06-prototype-methods/article.md +++ /dev/null @@ -1,259 +0,0 @@ - -# プロトタイプのためのメソッド - -このチャプターでは、プロトタイプを操作する追加のメソッドを説明します。 - -私たちがすでに知っている方法以外にも、プロトタイプを get/set する方法があります。 - -- [Object.create(proto[, descriptors])](mdn:js/Object/create) -- 与えられた `proto` を `[[Prototype]]` として、また任意のプロパティディスクリプタで空のオブジェクトを作ります。 -- [Object.getPrototypeOf(obj)](mdn:js/Object.getPrototypeOf) -- `obj` の `[[Prototype]]` を返します。 -- [Object.setPrototypeOf(obj, proto)](mdn:js/Object.setPrototypeOf) -- `obj` の `[[Prototype]]` に `proto` をセットします。 - -[cut] - -例: - -```js run -let animal = { - eats: true -}; - -// animal をプロトタイプとして新しいオブジェクトを作成する -*!* -let rabbit = Object.create(animal); -*/!* - -alert(rabbit.eats); // true -*!* -alert(Object.getPrototypeOf(rabbit) === animal); // rabbit のプロトタイプを取得 -*/!* - -*!* -Object.setPrototypeOf(rabbit, {}); // rabbit のプロトタイプを {} に変更 -*/!* -``` - -`Object.create` は任意の2つ目の引数を持っています: プロパティディスクリプタです。私たちはこのように、新しいオブジェクトに追加のプロパティを提供することができます。: - -```js run -let animal = { - eats: true -}; - -let rabbit = Object.create(animal, { - jumps: { - value: true - } -}); - -alert(rabbit.jumps); // true -``` - -ディスクリプタはチャプター で説明したのと同じフォーマットです。 - -`for..in` でプロパティをコピーするよりも、よりパワフルにオブジェクトをクローンするために `Object.create` を使うことができます。: - - -```js -// 完全に同一の obj の浅いクローン -let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); -``` - -この呼び出しはすべてのプロパティを含む `obj` の本当の正確なコピーを作ります。: 列挙型、非列挙型、データプロパティ、setter/getter -- すべてと、正確な `[[Prototype]]` です。 - -## 略史 - -もし `[[Prototype]]` を管理するすべての方法を数えると、たくさんあります! 同じことをするたくさんの方法があります! - -なぜでしょう? - -それは歴史的な理由のためです。 - -- コンストラクタ関数の `"prototype"` プロパティは非常に古代から機能しています。 -- 2012年後半: `Object.create` が標準に登場しました。それは与えられたプロトタイプでオブジェクトを作りますが、それを取得/設定することはできませんでした。なのでブラウザはいつでもプロトタイプの取得/設定ができる非標準の `__proto__` アクセサを実装しました。 -- 2015年後半: `Object.setPrototypeOf` と `Object.getPrototypeOf` が標準に追加されました。`__proto__` はどこでも実行されているデファクトだったので、標準の付録Bに追加されています。これはブラウザ以外の環境ではオプションです。 - -今のところ、私たちはすべての方法を自由に使い分けています。 - -技術的には、いつでも `[[Prototype]]` の取得/設定が可能です。しかし、通常はオブジェクト作成時に一度だけ設定を行い、変更はしません。: `rabbit` は `animal` から継承しており、それは変更しません。また、JavaScriptエンジンは高度に最適化されています。`Object.setPrototypeOf` または `obj.__proto__=` で "その場で" プロトタイプを変更することは、可能ですがとても遅い操作になります。 - - -## "非常にシンプルな" オブジェクト - -ご存知の通り、オブジェクトはキー/値ペアを格納するための連想配列として使うことができます。 - -...しかし、もしその中で *ユーザから提供された* キーを格納しようとした場合(例えばユーザが入力した辞書)、興味深い問題が起こります。: すべてのキーは `"__proto__"` を除いてうまく動作します。 - -例を確認してみましょう: - -```js run -let obj = {}; - -let key = prompt("What's the key?", "__proto__"); -obj[key] = "some value"; - -alert(obj[key]); // [object Object], "some value" ではありません! -``` - -もしユーザが `__proto__` を入力した場合、その代入は無視されます! - -それは驚くことではありません。`__proto__` プロパティは特別です: それはオブジェクトまたは `null` であり、文字列はプロトタイプにはなれません。 - -しかし、このような振る舞いを実装するつもりはありませんでした。私たちはキー/値ペアを格納したいですが、キー名が `"__proto__"` の場合は正しく保存されませんでした。なので、これはバグです。ここでの結果はひどくはありませんが、他のケースでは、プロトタイプは実際に変更される可能性があるため、処理が予期しない方向で間違ってしまう可能性があります。 - -最悪なのは -- 通常開発者はこのような可能性について全く考えません。これにより、このようなバグに気付きにくくなり、特にJavaScriptがサーバー側で使用されている場合には、それらを脆弱性に変えることさえあります。 - -このようなことは `__proto__` の場合にのみ起こります。すべての他のプロパティは通常 "代入可能" です。 - -どうやってこの問題を回避しましょう? - -最初に、`Map` を使うよう切り替えることができます。それですべて問題ありません。 - -しかし、 言語の作成者がその問題をずっと前から考えていたため、`Object` もまたここでは上手くいきます。 - -`__proto__` はオブジェクトのプロパティではなく、`Object.prototype` のアクセサプロパティです。: - -![](object-prototype-2.png) - -なので、もし `obj.__proto__` が読み込まれたり代入された場合、該当の getter/setter がそのプロトタイプから呼ばれ、それは `[[Prototype]]` の取得/設定をします。 - -最初に言ったとおり、`__proto__` は `[[Prototype]]` にアクセスする方法であり、`[[Prototype]]` 自身ではありません。 - -今、もし連想配列としてオブジェクトを使いたい場合、リテラルのトリックを使ってそれを行う事ができます。: - -```js run -*!* -let obj = Object.create(null); -*/!* - -let key = prompt("What's the key?", "__proto__"); -obj[key] = "some value"; - -alert(obj[key]); // "some value" -``` - -`Object.create(null)` はプロトタイプなし(`[[Prototype]]` が `null`)の空オブジェクトを作ります。: - -![](object-prototype-null.png) - -したがって、`__proto__` のための継承された getter/setter はありません。今や通常のデータプロパティとして処理されますので、上の例は正しく動作します。 - -このようなオブジェクトを "非常にシンプルな" または "純粋な辞書オブジェクト" と呼びます。なぜなら、それらは通常のオブジェクト `{...}` よりもシンプルなためです。 - -欠点は、そのようなオブジェクトには組み込みのオブジェクトメソッドがないことです。 `toString`: - -```js run -*!* -let obj = Object.create(null); -*/!* - -alert(obj); // Error (no toString) -``` - -...しかし、連想配列ではそれは通常問題ありません。 - -ほとんどのオブジェクトに関連したメソッドは `Object.keys(obj)` のように `Object.something(...)` であることに注意してください。 -- それらはプロトタイプにはないので、このようなオブジェクトで機能し続けます。: - - -```js run -let chineseDictionary = Object.create(null); -chineseDictionary.hello = "ni hao"; -chineseDictionary.bye = "zai jian"; - -alert(Object.keys(chineseDictionary)); // hello,bye -``` - -## すべてのプロパティを取得する - -オブジェクトから キー/値 を取得する多くの方法があります。 - -それらの1つはすでに知っています。: - -- [Object.keys(obj)](mdn:js/Object/keys) / [Object.values(obj)](mdn:js/Object/values) / [Object.entries(obj)](mdn:js/Object/entries) -- 列挙可能な自身の文字列プロパティ名/値/キー値ペアの配列を返します。それらのメソッドは *列挙可能な* プロパティで、*キーとして文字列を持つ* ものだけをリストします。 - -もしもシンボリックプロパティがほしい場合は: - -- [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) -- すべての自身のシンボリックプロパティ名の配列を返します。 - -非列挙型のプロパティが欲しい場合は: - -- [Object.getOwnPropertyNames(obj)](mdn:js/Object/getOwnPropertyNames) -- すべての自身の文字列プロパティ名を返します。 - -*すべて* のプロパティが欲しい場合は: - -- [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) -- すべての自身のプロパティ名の配列を返します。 - - -これらのメソッドは、どのプロパティが返されるかについて少し異なりますが、すべてがそのオブジェクト自身で動作します。プロトタイプからのプロパティはリストされません。 - -`for..in` ループは異なります。: それは継承されたプロパティもループします。 - -例: - -```js run -let animal = { - eats: true -}; - -let rabbit = { - jumps: true, - __proto__: animal -}; - -*!* -// 自身のキーのみ -alert(Object.keys(rabbit)); // jumps -*/!* - -*!* -// 継承されたキーも -for(let prop in rabbit) alert(prop); // jumps, eats -*/!* -``` - -もし継承されたプロパティを区別したい場合、組み込みのメソッド [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty) があります。: それは `obj` 自身が `key` という名前のプロパティをもつ(継承でない) 場合に `true` を返します。 - -だから、継承されたプロパティをフィルタリングすることができます。: - -```js run -let animal = { - eats: true -}; - -let rabbit = { - jumps: true, - __proto__: animal -}; - -for(let prop in rabbit) { - let isOwn = rabbit.hasOwnProperty(prop); - alert(`${prop}: ${isOwn}`); // jumps:true, then eats:false -} -``` -ここでは、私たちは次の継承のチェーンを持っています。: `rabbit`, 次に `animal` そして `Object.prototype` (`animal` はリテラルオブジェクト `{...}` なので、これはデフォルトです)、その上に `null` があります。: - -![](rabbit-animal-object.png) - -面白いことが1つあります。メソッド `rabbit.hasOwnProperty` はどこからきたでしょう?チェーンを見ると、`Object.prototype.hasOwnProperty` によってメソッドが提供されていることがわかります。言い換えると、それは継承されています。 - - -...しかしなぜ `hasOwnProperty` は `for..in` ループで現れないのでしょうか?答えはシンプルです。それは列挙可能ではありません。 `Object.prototype`の他のすべてのプロパティと同様です。だからこそそれらはリストに載っていません。 - -## サマリ - -このチャプターで説明したメソッドの簡単なリストを要約として示します。: - -- [Object.create(proto[, descriptors])](mdn:js/Object/create) -- 与えられた `proto` を `[[Prototype]]` (`null` もOK)として、また任意のプロパティディスクリプタで空のオブジェクトを作ります。 -- [Object.getPrototypeOf(obj)](mdn:js/Object.getPrototypeOf) -- `obj` の `[[Prototype]]` を返します( `__proto__` の getter と同じです)。 -- [Object.setPrototypeOf(obj, proto)](mdn:js/Object.setPrototypeOf) -- `obj` の `[[Prototype]]` に `proto` をセットします( `__proto__` の setter と同じです)。 -- [Object.keys(obj)](mdn:js/Object/keys) / [Object.values(obj)](mdn:js/Object/values) / [Object.entries(obj)](mdn:js/Object/entries) -- 列挙可能な自身の文字列プロパティ名/値/キー値ペアの配列を返します。 -- [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) -- すべての自身のシンボリックプロパティ名の配列を返します。 -- [Object.getOwnPropertyNames(obj)](mdn:js/Object/getOwnPropertyNames) -- すべての自身の文字列プロパティ名を返します。 -- [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) -- すべての自身のプロパティ名の配列を返します。 -- [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty): それは `obj` 自身が `key` という名前のプロパティをもつ(継承でない) 場合に `true` を返します。 - -私たちは `__proto__` は `[[Prototype]]` の getter/setterであり、他のメソッドと同様に `Object.prototype` に存在することも明らかにしました。 - -私たちは、`Object.create(null)` によってプロトタイプなしのオブジェクトを作ることができます。このようなオブジェクトは "純粋な辞書" として使われ、キーとして `"__proto__"` の問題はありません。 - -オブジェクトプロパティ(`Object.keys` など)を返すすべてのメソッドは -- "自身の" プロパティを返します。もし継承されたものが欲しい場合は、`for..in` を使います。 diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-2.png b/1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-2.png deleted file mode 100644 index ee42f6b98c..0000000000 Binary files a/1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-2.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-2@2x.png b/1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-2@2x.png deleted file mode 100644 index 1917c6631d..0000000000 Binary files a/1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-2@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-null.png b/1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-null.png deleted file mode 100644 index 7c2e3f9c78..0000000000 Binary files a/1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-null.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-null@2x.png b/1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-null@2x.png deleted file mode 100644 index fec1facb0b..0000000000 Binary files a/1-js/07-object-oriented-programming/06-prototype-methods/object-prototype-null@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/rabbit-animal-object.png b/1-js/07-object-oriented-programming/06-prototype-methods/rabbit-animal-object.png deleted file mode 100644 index 3254270f38..0000000000 Binary files a/1-js/07-object-oriented-programming/06-prototype-methods/rabbit-animal-object.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/rabbit-animal-object@2x.png b/1-js/07-object-oriented-programming/06-prototype-methods/rabbit-animal-object@2x.png deleted file mode 100644 index f794d7b84a..0000000000 Binary files a/1-js/07-object-oriented-programming/06-prototype-methods/rabbit-animal-object@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/1-inheritance-error-assign/solution.md b/1-js/07-object-oriented-programming/08-class-patterns/1-inheritance-error-assign/solution.md deleted file mode 100644 index 18d3ed969f..0000000000 --- a/1-js/07-object-oriented-programming/08-class-patterns/1-inheritance-error-assign/solution.md +++ /dev/null @@ -1,46 +0,0 @@ -エラーとなる行はここです: - -```js -Rabbit.prototype = Animal.prototype; -``` - -ここで、`Rabbit.prototype` と `Animal.prototype` は同じオブジェクトになります。なので、両方のクラスのメソッドがそのオブジェクトに混在します。 - -結果として、`Rabbit.prototyp.walk` は `Animal.prototype.walk` を上書きするので、すべての animals は跳ね始めます(bounce)。: - -```js run -function Animal(name) { - this.name = name; -} - -Animal.prototype.walk = function() { - alert(this.name + ' walks'); -}; - -function Rabbit(name) { - this.name = name; -} - -*!* -Rabbit.prototype = Animal.prototype; -*/!* - -Rabbit.prototype.walk = function() { - alert(this.name + " bounces!"); -}; - -*!* -let animal = new Animal("pig"); -animal.walk(); // pig bounces! -*/!* -``` - -正しいバリアントは次の通りです: - -```js -Rabbit.prototype.__proto__ = Animal.prototype; -// もしくはこのような形です: -Rabbit.prototype = Object.create(Animal.prototype); -``` - -これによりプロトタイプは分離され、それぞれ対応するクラスのメソッドを格納します。が、`Rabbit.prototype` は `Animal.prototype` を継承します。 diff --git a/1-js/07-object-oriented-programming/08-class-patterns/1-inheritance-error-assign/task.md b/1-js/07-object-oriented-programming/08-class-patterns/1-inheritance-error-assign/task.md deleted file mode 100644 index 7a2020125b..0000000000 --- a/1-js/07-object-oriented-programming/08-class-patterns/1-inheritance-error-assign/task.md +++ /dev/null @@ -1,29 +0,0 @@ -importance: 5 - ---- - -# 継承でのエラー - -下のプロトタイプ継承でのエラーを見つけてください。 - -何が間違っていますか?どのような結果になるでしょうか? - -```js -function Animal(name) { - this.name = name; -} - -Animal.prototype.walk = function() { - alert(this.name + ' walks'); -}; - -function Rabbit(name) { - this.name = name; -} - -Rabbit.prototype = Animal.prototype; - -Rabbit.prototype.walk = function() { - alert(this.name + " bounces!"); -}; -``` diff --git a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/solution.md b/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/solution.md deleted file mode 100644 index bf38842656..0000000000 --- a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/solution.md +++ /dev/null @@ -1,2 +0,0 @@ -関数型での内部のプロパティ(`template`, `timer`) と内部メソッド `render` はアンダースコア `_` でプライベートとしてマークされていることに注意してください。 - diff --git a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/solution.view/clock.js b/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/solution.view/clock.js deleted file mode 100644 index 7a193b79d0..0000000000 --- a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/solution.view/clock.js +++ /dev/null @@ -1,32 +0,0 @@ -function Clock({ template }) { - this._template = template; -} - -Clock.prototype._render = function() { - let date = new Date(); - - let hours = date.getHours(); - if (hours < 10) hours = '0' + hours; - - let mins = date.getMinutes(); - if (mins < 10) min = '0' + mins; - - let secs = date.getSeconds(); - if (secs < 10) secs = '0' + secs; - - let output = this._template - .replace('h', hours) - .replace('m', mins) - .replace('s', secs); - - console.log(output); -}; - -Clock.prototype.stop = function() { - clearInterval(this._timer); -}; - -Clock.prototype.start = function() { - this._render(); - this._timer = setInterval(() => this._render(), 1000); -}; diff --git a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/clock.js b/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/clock.js deleted file mode 100644 index 26081a35a1..0000000000 --- a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/clock.js +++ /dev/null @@ -1,34 +0,0 @@ -function Clock({ template }) { - - let timer; - - function render() { - let date = new Date(); - - let hours = date.getHours(); - if (hours < 10) hours = '0' + hours; - - let mins = date.getMinutes(); - if (mins < 10) min = '0' + mins; - - let secs = date.getSeconds(); - if (secs < 10) secs = '0' + secs; - - let output = template - .replace('h', hours) - .replace('m', mins) - .replace('s', secs); - - console.log(output); - } - - this.stop = function() { - clearInterval(timer); - }; - - this.start = function() { - render(); - timer = setInterval(render, 1000); - }; - -} diff --git a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/task.md b/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/task.md deleted file mode 100644 index 09159d39b9..0000000000 --- a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/task.md +++ /dev/null @@ -1,9 +0,0 @@ -importance: 5 - ---- - -# プロトタイプに書き直す - -`Clock` クラスは関数スタイルで書かれています。プロトタイプを使って書き直してください。 - -P.S. 時計はコンソールで動きます、開いて見てください。 diff --git a/1-js/07-object-oriented-programming/08-class-patterns/article.md b/1-js/07-object-oriented-programming/08-class-patterns/article.md deleted file mode 100644 index d4380d6da5..0000000000 --- a/1-js/07-object-oriented-programming/08-class-patterns/article.md +++ /dev/null @@ -1,243 +0,0 @@ - -# Class パターン - -```quote author="Wikipedia" -オブジェクト指向プログラミングでは、*クラス* はオブジェクト生成、状態(メンバ変数)の初期値の提供や振る舞いの実装(メンバ関数またはメソッド)のための拡張可能なプログラムコードテンプレートです。 -``` - -JavaScriptには特別な構文構文とキーワード `class` があります。しかしそれを学ぶ前に、 "クラス" という言葉はオブジェクト指向プログラミングの理論に由来すると考えるべきでしょう。定義は上記で引用されている通り、言語に依存しません。。 - -JavaScriptでは、`class` キーワードを使わずにクラスを作成する、いくつかのよく知られたプログラミングパターンがあります。そしてここでは最初にそれについて話しましょう。 - -`class` の構造は次のチャプターで説明しますが、JavaScriptでは、それは "シンタックスシュガー" であり、ここで学ぶパターンの1つの拡張です。 - -[cut] - - -## 関数クラスパターン - -下のコンストラクタ関数は定義に従って、"クラス" と考えることができます。: - -```js run -function User(name) { - this.sayHi = function() { - alert(name); - }; -} - -let user = new User("John"); -user.sayHi(); // John -``` - -それは定義のすべての部分に従います: - -1. オブジェクトを作成する (`new` で呼び出し可能) ための "プログラムコードテンプレート" である。 -2. 状態 (パラメータから `name`) の初期値を提供する。 -3. メソッド (`sayHi`) を提供する。 - -これは、*関数的なクラスパターン(functional class pattern)* と呼ばれます。 - -関数的なクラスパターンでは、`this` に割り当てられていない `User` の中のローカル変数とネストされた関数は内側からは見えますが、外のコードからはアクセスできません。 - -なので、私たちは簡単に内部関数や変数を追加することができます。次の `calcAge()` のように: - -```js run -function User(name, birthday) { - -*!* - // User 内の他のメソッドからのみ見えます - function calcAge() { - return new Date().getFullYear() - birthday.getFullYear(); - } -*/!* - - this.sayHi = function() { - alert(name + ', age:' + calcAge()); - }; -} - -let user = new User("John", new Date(2000,0,1)); -user.sayHi(); // John -``` - -このコードでは、変数 `name`, `birthday` と関数 `calcAge()` はオブジェクトに対しては内部、*private* です。それはその内側からのみ見えます。 - -一方、`sayHi` は外部、*public* メソッドです。`user` を作成する外部コードはそれにアクセスできます。 - -このようにして、内部実装の詳細とヘルパーメソッドを外部コードから隠すことができます。 `this`に割り当てられたものだけが外側に見えるようになります。 - -## ファクトリークラスパターン - -私たちは、`new` を全く使わずにクラスを生成することができます。 - -このようになります: - -```js run -function User(name, birthday) { - // User 内の他のメソッドからのみ見えます - function calcAge() { - return new Date().getFullYear() - birthday.getFullYear(); - } - - return { - sayHi() { - alert(name + ', age:' + calcAge()); - } - }; -} - -*!* -let user = User("John", new Date(2000,0,1)); -*/!* -user.sayHi(); // John -``` - -ご覧の通り、関数 `User` はパブリックなプロパティとメソッドを持つオブジェクトを返します。このメソッドの唯一のメリットは、`new` を省略できることです。: `let user = new User(...)` の代わりに `let user = User(...)` と書きます。別の側面では、ほとんど関数的なパターンと同じです。 - -## プロトタイプベースのクラス - -プロトタイプベースのクラスは最も重要で一般的にベストです。 実際には、関数的なクラスパターンとファクトリクラスパターンはほとんど使用されません。 - -後ほどなぜかをお見せします。 - -ここでは、プロトタイプを使って同じクラスを再度書いています。: - -```js run -function User(name, birthday) { -*!* - this._name = name; - this._birthday = birthday; -*/!* -} - -*!* -User.prototype._calcAge = function() { -*/!* - return new Date().getFullYear() - this._birthday.getFullYear(); -}; - -User.prototype.sayHi = function() { - alert(this._name + ', age:' + this._calcAge()); -}; - -let user = new User("John", new Date(2000,0,1)); -user.sayHi(); // John -``` - -コード構造: - -- コンストラクタ `User` は現在のオブジェクトの状態のみを初期化します。 -- メソッドは `User.prototype` に追加されています。 - -ご覧の通り、メソッドは字句的に `function User` の中にはなく、共通のレキシカル環境を共有しません。もし `function User` の内側で変数を宣言した場合、それらはメソッドには見えません。 - -従って、内部プロパティとメソッドはアンダースコア `"_"` が先頭に追加されているという、広く知られている合意があります。`_name` または `_calcAge()` のように。技術的にはそれは単なる合意であり、外部コードは依然としてそれらにアクセスすることはできます。しかし、ほとんどの開発者は `"_"` の意味を理解しており、外部コードの中でプロフィックスのついたプロパティやメソッドを触らないようにしています。 - -関数的なパターンと比較した時の利点は次の通りです。: - -- 関数的なパターンでは、各オブジェクトにはすべてのメソッドの独自のコピーがあります。 コンストラクタで `this.sayHi = function(){...}` と他のメソッドの別のコピーを割り当てます。 -- プロトタイプ的なパターンでは、すべてのメソッドはすべての user オブジェクトで共有される `User.prototype` にあります。オブジェクト自身はデータだけを保持しています。 - -従って、プロトタイプパターンはよりメモリ効率が良いです。 - -...しかし、それだけではありません。プロトタイプを使うと継承を効率的にセットアップすることができます。すべての組み込みのJavaScriptオブジェクトはプロトタイプを使っています。また特別な構文構造があります: "class" は、見た目の良い構文を提供します。 そしてもっと多くのことがありますので、それらと一緒に進んでみましょう。 - -## クラスのためのプロトタイプベースの継承 - -2つのプロトタイプベースのクラスを持っているとしましょう。 - -`Rabbit`: - -```js -function Rabbit(name) { - this.name = name; -} - -Rabbit.prototype.jump = function() { - alert(this.name + ' jumps!'); -}; - -let rabbit = new Rabbit("My rabbit"); -``` - -![](rabbit-animal-independent-1.png) - -...そして `Animal`: - -```js -function Animal(name) { - this.name = name; -} - -Animal.prototype.eat = function() { - alert(this.name + ' eats.'); -}; - -let animal = new Animal("My animal"); -``` - -![](rabbit-animal-independent-2.png) - -今はそれらは完全に独立しています。 - -しかし、私たちは `Rabbit` は `Animal` を拡張させたものにしたいです。言い換えると、rabbits は animals をベースとし、`Animal` のメソッドへのアクセスを持ち、自身のメソッドでそれを拡張する必要があります。 - -プロトタイプの言語ではどういう意味でしょうか? - -今、`rabbit` オブジェクトのメソッドは `Rabbit.prototype` にあります。`Rabbit.prototype` にメソッドが見つからない場合, `rabbit` に "フォールバック" として `Animal.prototype` を使ってほしいです。 - -なので、プロトタイプチェーンは `rabbit` -> `Rabbit.prototype` -> `Animal.prototype` である必要があります。 - -このように: - -![](class-inheritance-rabbit-animal.png) - -それを実装するコードは次の通りです: - -```js run -// 以前と同じ Animal -function Animal(name) { - this.name = name; -} - -// すべての animals 食べることができますよね? -Animal.prototype.eat = function() { - alert(this.name + ' eats.'); -}; - -// 以前と同じ Rabbit -function Rabbit(name) { - this.name = name; -} - -Rabbit.prototype.jump = function() { - alert(this.name + ' jumps!'); -}; - -*!* -// 継承チェーンを設定します -Rabbit.prototype.__proto__ = Animal.prototype; // (*) -*/!* - -let rabbit = new Rabbit("White Rabbit"); -*!* -rabbit.eat(); // rabbits も食べることができる -*/!* -rabbit.jump(); -``` - -行 `{*}` はプロトタイプチェーンを設定します。つまり、`rabbit` はまず `Rabbit.prototype` のメソッドを検索し、次に `Animal.prototype` を検索します。そして、完全性のために、もしメソッドが `Animal.prototype` に存在しない場合、`Object.prototype` で検索を継続します。`Animal.prototype` は通常のオブジェクトなので、それを継承しています。 - -ここはその完全なイメージです。: - -![](class-inheritance-rabbit-animal-2.png) - -## サマリ - -"クラス" の言葉はオブジェクト指向プログラミングからきています。JavaScriptでは、それは通常関数クラスパターンまたはプロトタイプパターンを意味します。プロトタイプパターンはより協力でメモリ効率がよいので、それを使うことを推奨します。 - -プロトタイプのパターンによれば: -1. メソッドは `Class.prototype` に格納されます。 -2. プロトタイプはお互いから継承します。 - -次のチャプターでは、`class` キーワードと構造について学びます。プロトタイプクラスを短く書くことができ、いくつかの追加の利点があります。 diff --git a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2.png b/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2.png deleted file mode 100644 index ad4a409322..0000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2@2x.png b/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2@2x.png deleted file mode 100644 index 199ed3ee60..0000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal-2@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal.png b/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal.png deleted file mode 100644 index 70708c2848..0000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal@2x.png b/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal@2x.png deleted file mode 100644 index 0db1301815..0000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/class-inheritance-rabbit-animal@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1.png b/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1.png deleted file mode 100644 index e63d7d7844..0000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1@2x.png b/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1@2x.png deleted file mode 100644 index 3d1be9ccef..0000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-1@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2.png b/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2.png deleted file mode 100644 index 435ec5f89f..0000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2@2x.png b/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2@2x.png deleted file mode 100644 index 5731da73a8..0000000000 Binary files a/1-js/07-object-oriented-programming/08-class-patterns/rabbit-animal-independent-2@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/index.html b/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/index.html deleted file mode 100644 index fdee13d01b..0000000000 --- a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Console clock - - - - - - - - - - diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/index.html b/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/index.html deleted file mode 100644 index fdee13d01b..0000000000 --- a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Console clock - - - - - - - - - - diff --git a/1-js/07-object-oriented-programming/09-class/animal-rabbit-extends.png b/1-js/07-object-oriented-programming/09-class/animal-rabbit-extends.png deleted file mode 100644 index 2db88f3663..0000000000 Binary files a/1-js/07-object-oriented-programming/09-class/animal-rabbit-extends.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/09-class/animal-rabbit-extends@2x.png b/1-js/07-object-oriented-programming/09-class/animal-rabbit-extends@2x.png deleted file mode 100644 index 9539fe9ec6..0000000000 Binary files a/1-js/07-object-oriented-programming/09-class/animal-rabbit-extends@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/09-class/article.md b/1-js/07-object-oriented-programming/09-class/article.md deleted file mode 100644 index 62abb782e3..0000000000 --- a/1-js/07-object-oriented-programming/09-class/article.md +++ /dev/null @@ -1,356 +0,0 @@ - -# クラス - -"class" 構造は、綺麗で見やすい構文でプロトタイプベースのクラスを定義することができます。 - -[cut] - -## "class" 構文 - -`class` 構文は汎用性があり、最初にシンプルな例から始めます。 - -これはプロトタイプベースのクラス `User` です: - -```js run -function User(name) { - this.name = name; -} - -User.prototype.sayHi = function() { - alert(this.name); -} - -let user = new User("John"); -user.sayHi(); -``` - -...そしてこれは `class` 構文を使った場合です: - -```js run -class User { - - constructor(name) { - this.name = name; - } - - sayHi() { - alert(this.name); - } - -} - -let user = new User("John"); -user.sayHi(); -``` - -2つの例が似ていることは容易に分かると思います。クラス内のメソッドはそれらの間にカンマを持たないことに注意してください。新米の開発者はときどきそれを忘れて、クラスメソッドの間にカンマをおいてしまい動作しなくなります。これはリテラルオブジェクトではなく、クラス構文です。 - -では、`class` は正確になにをするでしょう? それが新しい言語レベルの実態を定義していると思うかもしれませんが、それは間違っています。 - -ここで `class User {...}` は実際には2つのことをしています。: - -1. `"constructor"` という名前の関数を参照する変数 `User` を宣言します。 -2. その定義の中にリストされているメソッドを `User.prototype` の中に置きます。ここでは、`sayHi` と `constructor` です。 - -次のコードでクラスを掘り下げてみましょう。: - -```js run -class User { - constructor(name) { this.name = name; } - sayHi() { alert(this.name); } -} - -*!* -// 証明: User は "constructor" 関数である -*/!* -alert(User == User.prototype.constructor); // true - -*!* -// 証明: その "prototype" には 2つのメソッドがある -*/!* -alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi -``` - -これは `class User` が生成するものの図です。: - -![](class-user.png) - - -従って、`class` はコンストラクタとプロトタイプメソッドを一緒に定義する特別な構文です。 - -...しかしそれだけではありません。小さな微調整があちこちにあります。: - -コンストラクタは `new` を必要とします -: 通常の関数とは異なり、クラス `constructor` は `new` なしで呼ぶことはできません。: - -```js run -class User { - constructor() {} -} - -alert(typeof User); // function -User(); // Error: Class コンストラクタ User は `new` なしで呼べません -``` - -異なる文字列出力 -: もし `alert(User)` のように出力すると、エンジンによって `"class User..."` や `"function User..."` と表示されます。 - -混乱しないでください: 文字列の表現は様々ですが、それは依然として関数であり、JavaScript言語で別の "class" エンティティはありません。 - -クラスメソッドは非列挙型 -: クラス定義は、`"prototype"` の中のすべてのメソッドに対して `enumerable` フラグを `false` にセットします。オブジェクトを `for..in` したとき、通常クラスメソッドは出てきてほしくないので、これは良いことです。 - -クラスはデフォルトの `constructor() {}` を持っています -: `class` 構造の中に `constructor` がない場合、空の関数が生成され `constructor() {}` と書いたのと同じように動作します。 - -クラスは常に `use strict` です -: クラス構造の内側のすべてのコードは自動的に strict モードです。 - -### Getters/setters - -クラスは getter/setter も含みます。以下はそれらを使って実装した `user.name` の例です。: - -```js run -class User { - - constructor(name) { - // setter を呼び出す - this.name = name; - } - -*!* - get name() { -*/!* - return this._name; - } - -*!* - set name(value) { -*/!* - if (value.length < 4) { - alert("Name too short."); - return; - } - this._name = value; - } - -} - -let user = new User("John"); -alert(user.name); // John - -user = new User(""); // Name too short. -``` - -内部的に、getter と setter もまた次のように `User` プロトタイプ上に作られます。: - -```js -Object.defineProperty(User.prototype, { - name: { - get() { - return this._name - }, - set(name) { - // ... - } - } -}); -``` - -### メソッドのみ - -オブジェクトリテラルとは異なり、`class` の中で `property:value` 割り当ては許可していません。メソッドとgetter/setterのみです。その制限を緩和するために、仕様で進行中のものがいくつかありますが、それはまだありません。 - -もしプロトタイプに非関数の値を置く必要が本当にある場合、`prototype` を手動で修正することができます。以下を見てください: - -```js run -class User { } - -User.prototype.test = 5; - -alert( new User().test ); // 5 -``` - -従って、技術的にはそれは可能です。が、なぜそうしているかを知るべきです。このようなプロパティはクラスのすべてのオブジェクトの間で共有されます。 - -"クラス内" の代替は getter を使うことです。: - -```js run -class User { - get test() { - return 5; - } -} - -alert( new User().test ); // 5 -``` - -外部コードからの使い方は同じですが、getter バリアントは少し遅いです。 - -## クラス表現 - -関数と同様に、クラスは別の式の中で定義され、渡され、返されます。 - -これは、クラスを返す関数("クラスファクトリー")です。: - -```js run -function makeClass(phrase) { -*!* - // class を宣言しそれを返します - return class { - sayHi() { - alert(phrase); - }; - }; -*/!* -} - -let User = makeClass("Hello"); - -new User().sayHi(); // Hello -``` - -`class` はプロトタイプ付き関数の特別な形式であることを思い出すと、これはとても普通です。 - -また、名前付けされた関数表現のように、このようなクラスもまた名前を保つ場合があります。それはクラスの中でのみ見えます。: - -```js run -// "名前付けされたクラス式" (そのような言葉はありませんが) -let User = class *!*MyClass*/!* { - sayHi() { - alert(MyClass); // MyClass は class の内側でだけ見えます - } -}; - -new User().sayHi(); // 動作します, MyClass の定義を表示します - -alert(MyClass); // error, MyClass は class の外からは見えません -``` - -## 静的メソッド(Static method) - -私たちは、その `"prototype"` ではなく、クラス関数へメソッドを割り当てることもできます。このようなメソッドは *static* と呼ばれます。 - -例: - -```js run -class User { -*!* - static staticMethod() { -*/!* - alert(this == User); - } -} - -User.staticMethod(); // true -``` - -これは実際には、関数プロパティとして代入するのと同じです。: - -```js -function User() { } - -User.staticMethod = function() { - alert(this == User); -}; -``` - -`User.staticMethod()` の中での `this` の値はクラスコンストラクタ `User` 自身です("ドットの前のオブジェクト" ルールです)。 - -通常、静的メソッドは特定のオブジェクトに依存せず、クラスに属する関数を実装するときに使われます。 - -例えば、私たちは `Article` オブジェクトを持っており、それらを比較する関数が必要とします。自然な選択だと、このように `Article.compare` になるでしょう。: - -```js run -class Article { - constructor(title, date) { - this.title = title; - this.date = date; - } - -*!* - static compare(articleA, articleB) { - return articleA.date - articleB.date; - } -*/!* -} - -// 使い方 -let articles = [ - new Article("Mind", new Date(2016, 1, 1)), - new Article("Body", new Date(2016, 0, 1)), - new Article("JavaScript", new Date(2016, 11, 1)) -]; - -*!* -articles.sort(Article.compare); -*/!* - -alert( articles[0].title ); // Body -``` - -ここで、`Article.compare` は記事を比較するための手段として記事の "上に" 立っています。それは記事のメソッドではなく、クラス全体のメソッドです。 - -別の例は、いわゆる "ファクトリー" メソッドです。イメージしてください、記事を作成する方法はほとんどありません。: - -1. 与えられたパラメータ(`title`, `date` など)による作成 -2. 今日の日付の空の記事の作成 -3. ... - -最初の方法はコンストラクタで実装することができます。また2つ目の方法としてクラスの静的メソッドを作ることができます。 - -ここでの `Article.createTodays()` のように: - -```js run -class Article { - constructor(title, date) { - this.title = title; - this.date = date; - } - -*!* - static createTodays() { - // 思い出してください, this = Article - return new this("Todays digest", new Date()); - } -*/!* -} - -let article = Article.createTodays(); - -alert( article.title ); // 今日のダイジェスト -``` - -これで今日のダイジェストを作成する必要があるたびに、`Article.createTodays()` を呼ぶことができます。もう一度、これは記事のメソッドではなく、クラス全体のメソッドです。 - -静的メソッドは、次のように、データベース関連のクラスでデータベースの検索/保存/削除のためにも使用されます。: - -```js -// Article は記事を管理するための特別なクラスと仮定します -// 記事を削除するための static メソッド: -Article.remove({id: 12345}); -``` - -## サマリ - -基本のクラス構文はこのようになります。: - -```js -class MyClass { - constructor(...) { - // ... - } - method1(...) {} - method2(...) {} - get something(...) {} - set something(...) {} - static staticMethod(..) {} - // ... -} -``` - -`MyClass` の値は `constructor` として提供された関数です。もし `constructor` がなければ、空の関数です。 - -いずれにしても、クラス宣言に列挙されたメソッドは `prototype`のメンバーになりますが、静的メソッドは関数自身に書き込まれ、`MyClass.staticMethod()` として呼び出すことができます。 静的メソッドは、クラスに結びつく関数が必要なときに使用されますが、そのクラスのオブジェクトに結びつく場合には使用されません。 - -次のチャプターでは、継承を含め、よりクラスについて学びます。 diff --git a/1-js/07-object-oriented-programming/09-class/class-user.png b/1-js/07-object-oriented-programming/09-class/class-user.png deleted file mode 100644 index 5579e6bbdf..0000000000 Binary files a/1-js/07-object-oriented-programming/09-class/class-user.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/09-class/class-user@2x.png b/1-js/07-object-oriented-programming/09-class/class-user@2x.png deleted file mode 100644 index 5a85e6589d..0000000000 Binary files a/1-js/07-object-oriented-programming/09-class/class-user@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/clock.js b/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/clock.js deleted file mode 100644 index c710b9da9b..0000000000 --- a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/clock.js +++ /dev/null @@ -1,34 +0,0 @@ -class Clock { - constructor({ template }) { - this._template = template; - } - - _render() { - let date = new Date(); - - let hours = date.getHours(); - if (hours < 10) hours = '0' + hours; - - let mins = date.getMinutes(); - if (mins < 10) min = '0' + mins; - - let secs = date.getSeconds(); - if (secs < 10) secs = '0' + secs; - - let output = this._template - .replace('h', hours) - .replace('m', mins) - .replace('s', secs); - - console.log(output); - } - - stop() { - clearInterval(this._timer); - } - - start() { - this._render(); - this._timer = setInterval(() => this._render(), 1000); - } -} diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/clock.js b/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/clock.js deleted file mode 100644 index c710b9da9b..0000000000 --- a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/clock.js +++ /dev/null @@ -1,34 +0,0 @@ -class Clock { - constructor({ template }) { - this._template = template; - } - - _render() { - let date = new Date(); - - let hours = date.getHours(); - if (hours < 10) hours = '0' + hours; - - let mins = date.getMinutes(); - if (mins < 10) min = '0' + mins; - - let secs = date.getSeconds(); - if (secs < 10) secs = '0' + secs; - - let output = this._template - .replace('h', hours) - .replace('m', mins) - .replace('s', secs); - - console.log(output); - } - - stop() { - clearInterval(this._timer); - } - - start() { - this._render(); - this._timer = setInterval(() => this._render(), 1000); - } -} diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/task.md b/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/task.md deleted file mode 100644 index 9423c2cb01..0000000000 --- a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/task.md +++ /dev/null @@ -1,12 +0,0 @@ -importance: 5 - ---- - -# 拡張された時計 - -私たちは `Clock` クラスを持っています。今のところ、毎秒時間を表示します。 - -`Clock` を継承した新たなクラス `ExtendedClock` を作成し、`precision` パラメータを追加してください -- "時計のカチカチ" の間の `ms` の数値です。デフォルtおでは `1000` (1秒) です。 - -- あなたのコードはファイル `extended-clock.js` にしてください。 -- オジリナルの `clock.js` は変更しないでください。それを拡張してください。 diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object.png b/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object.png deleted file mode 100644 index d4ff37e561..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png deleted file mode 100644 index a54a9d2f86..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/rabbit-extends-object@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/solution.md b/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/solution.md deleted file mode 100644 index 8012027592..0000000000 --- a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/solution.md +++ /dev/null @@ -1,87 +0,0 @@ -解答は2つパートがあります。 - -最初に、簡単な方は、継承しているクラスはコンストラクタで `super()` を呼ぶ必要があるということです。そうでなければ `"this"` が "定義済み" になりません。 - -なので、次のように直します: - -```js run -class Rabbit extends Object { - constructor(name) { -*!* - super(); // 継承しているとき、親コンストラクタを呼ぶ必要があります -*/!* - this.name = name; - } -} - -let rabbit = new Rabbit("Rab"); - -alert( rabbit.hasOwnProperty('name') ); // true -``` - -しかし、これですべてではありません。 - -修正した後でさえ、`"class Rabbit extends Object"` 対 `class Rabbit` では依然として重要な違いがあります。 - -知っている通り、 "extends" 構文は2つのプロトタイプを設定します。: - -1. コンストラクタ関数の `"prototype"` 間(メソッド用) -2. コンストラクタ関数自身の間(静的メソッド用) - -我々のケースでは、`class Rabbit extends Object` では次を意味します: - -```js run -class Rabbit extends Object {} - -alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true -alert( Rabbit.__proto__ === Object ); // (2) true -``` - -従って、このように `Rabbit` 経由で `Object` の静的メソッドにアクセスすることができます。: - -```js run -class Rabbit extends Object {} - -*!* -// 通常は Object.getOwnPropertyNames と呼びます -alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // a,b -*/!* -``` - -また、`extends` を使わない場合 `class Rabbit` は2つ目の参照を持ちません。 - -比較してみてください: - -```js run -class Rabbit {} - -alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true -alert( Rabbit.__proto__ === Object ); // (2) false (!) - -*!* -// エラー、Rabbit にこのような関数はありません -alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Error -*/!* -``` - -シンプルな `class Rabbit` では `Rabbit` 関数は同じプロトタイプを持っています。 - -```js run -class Rabbit {} - -// (2) の代わりに、これは正しいです: -alert( Rabbit.__proto__ === Function.prototype ); -``` - -ところで、`Function.prototype` は "一般的な" 関数メソッドを持っています。例えば `call`, `bind` などです。それらは究極的には両方のケースで利用可能です。なぜなら、組み込みの `Object` コンストラクタに対して、`Object.__proto__ === Function.prototype` だからです。 - -これはその図です: - -![](rabbit-extends-object.png) - -従って、まとめると2つの違いがあります。: - -| class Rabbit | class Rabbit extends Object | -|--------------|------------------------------| -| -- | コンストラクタで `super()` を呼ぶ必要がある | -| `Rabbit.__proto__ === Function.prototype` | `Rabbit.__proto__ === Object` | diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static.png b/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static.png deleted file mode 100644 index 998c823300..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static@2x.png deleted file mode 100644 index 98a80d38fa..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/animal-rabbit-static@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/article.md b/1-js/07-object-oriented-programming/10-class-inheritance/article.md deleted file mode 100644 index 87a77e63d5..0000000000 --- a/1-js/07-object-oriented-programming/10-class-inheritance/article.md +++ /dev/null @@ -1,578 +0,0 @@ - -# クラスの継承, super - -クラスは別のクラスに拡張することができます。 技術的には、プロトタイプの継承に基づいた素晴らしい構文があります。 - -別のクラスから継承するためには、`"extends"` と括弧 `{..}` の前に親クラスを指定する必要があります。 - -[cut] - -ここでは、`Rabbit` は `Animal` から継承しています: - -```js run -class Animal { - - constructor(name) { - this.speed = 0; - this.name = name; - } - - run(speed) { - this.speed += speed; - alert(`${this.name} runs with speed ${this.speed}.`); - } - - stop() { - this.speed = 0; - alert(`${this.name} stopped.`); - } - -} - -*!* -// Animal から継承 -class Rabbit extends Animal { - hide() { - alert(`${this.name} hides!`); - } -} -*/!* - -let rabbit = new Rabbit("White Rabbit"); - -rabbit.run(5); // White Rabbit runs with speed 5. -rabbit.hide(); // White Rabbit hides! -``` - -`extends` キーワードは、実際には `Rabbit.prototype` から `Animal.prototype` へ `[[Prototype]]` 参照を追加します。 - -![](animal-rabbit-extends.png) - -なので、現在 `rabbit` は自身のメソッドと `Animal` のメソッド両方へのアクセスを持ちます。 - -````smart header="`extends` の後では任意の式が指定できます" -クラス構文では単にクラスではなく、`extends` の後に任意の式を指定することができます。 - -例えば、親クラスを生成する関数呼び出します: - -```js run -function f(phrase) { - return class { - sayHi() { alert(phrase) } - } -} - -*!* -class User extends f("Hello") {} -*/!* - -new User().sayHi(); // Hello -``` -ここでは、 `class User` は `f("Hello")` の結果を継承しています。 - -多くの条件に依存したクラスを生成するための関数を使用し、それらから継承できるような高度なプログラミングパターンに対して、これは役立つ場合があります。 -```` - -## メソッドのオーバーライド - -では、前に進めてメソッドをオーバライドをしてみましょう。今のところ、`Rabbit` は `Animal` から `this.speed = 0` をセットする `stop` メソッドを継承しています。 - -もし `Rabbit` で自身の `stop` を指定すると、代わりにそれが使われるようになります。: - -```js -class Rabbit extends Animal { - stop() { - // ...これは rabbit.stop() のために使われる - } -} -``` - - -...しかし、通常は親メソッドを完全に置き換えるのではなく、その上に組み立てて、その機能の微調整または拡張を行うことを望んできます。私たちはメソッド内で何かをしますが、その前後またはその処理の中で親メソッドを呼び出します。 - -クラスはそのために `"super"` キーワードを提供しています。 - -- `super.method(...)` は親メソッドを呼び出します。 -- `super(...)` は親のコンストラクタを呼び出します(我々のコンスタクタの内側でのみ)。 - -例えば、私たちのうさぎが止まったとき自動的に隠れさせましょう。: - -```js run -class Animal { - - constructor(name) { - this.speed = 0; - this.name = name; - } - - run(speed) { - this.speed += speed; - alert(`${this.name} runs with speed ${this.speed}.`); - } - - stop() { - this.speed = 0; - alert(`${this.name} stopped.`); - } - -} - -class Rabbit extends Animal { - hide() { - alert(`${this.name} hides!`); - } - -*!* - stop() { - super.stop(); // 親の stop 呼び出し - this.hide(); // その後隠す - } -*/!* -} - -let rabbit = new Rabbit("White Rabbit"); - -rabbit.run(5); // White Rabbit runs with speed 5. -rabbit.stop(); // White Rabbit stopped. White rabbit hides! -``` - -これで `Rabbit` は処理の中で親 `super.stop()` を呼び出す `stop` メソッドを持っています。 - -````smart header="アロー関数は `super` を持っていません" -チャプター で述べた通り、アロー関数には `super` がありません。 - -もしアクセスすると、外部の関数から取得されます。例えば: -```js -class Rabbit extends Animal { - stop() { - setTimeout(() => super.stop(), 1000); // 1秒後親の stop 実行 - } -} -``` - -アロー関数での `super` は `stop()` での `super` と同じです。なので、意図通りに動きます。ここのように "通常の" 関数を指定すると、エラーになります。: - -```js -// Unexpected super -setTimeout(function() { super.stop() }, 1000); -``` -```` - - -## コンストラクタのオーバライド - -コンストラクタに対しては、少し用心が必要です。 - -今まで、`Rabbit` は自身の `constructor` を持っていませんでした。 - - -[仕様(specification)](https://tc39.github.io/ecma262/#sec-runtime-semantics-classdefinitionevaluation)によると、クラスが別のクラスを拡張し、`constructor` を持たない場合、次のような `constructor` が生成されます。 - -```js -class Rabbit extends Animal { - // 独自のコンストラクタを持たないクラスを拡張するために生成されます -*!* - constructor(...args) { - super(...args); - } -*/!* -} -``` - -ご覧の通り、基本的にはすべての引数を渡して親の `constructor` を呼び出します。それは自身のコンストラクタを書いていない場合に起こります。 -では、カスタムのコンストラクタを `Rabbit` に追加してみましょう。それは `name` に加えて `earLength` を指定します。: - -```js run -class Animal { - constructor(name) { - this.speed = 0; - this.name = name; - } - // ... -} - -class Rabbit extends Animal { - -*!* - constructor(name, earLength) { - this.speed = 0; - this.name = name; - this.earLength = earLength; - } -*/!* - - // ... -} - -*!* -// 動作しません! -let rabbit = new Rabbit("White Rabbit", 10); // Error: this は定義されていません -*/!* -``` - -おっと! エラーになりました。これではうさぎを作ることができません。何が間違っていたのでしょう? - -簡単な回答: 継承したクラスのコンストラクタは `super(...)` を呼び出し、(!) `this` を使う前にそれを行わなければなりません。 - -...しかしなぜ? ここで何が起きているのでしょう? 確かにこの要件は奇妙に見えます。 - -もちろん、それへの説明があります。詳細を見てみましょう。それであなたは何が起こっているのかを本当に理解するでしょう。 - -JavaScriptでは、"継承しているクラスのコンストラクタ関数" とその他すべてで区別があります。継承しているクラスでは、該当するコンストラクタ関数は特別な内部プロパティ `[[ConstructorKind]]:"derived"` が付けられます。 - -違いは: - -- 通常のコンストラクタを実行するとき、`this` として空のオブジェクトを作り、それを続けます。 -- しかし、派生したコンストラクタが実行されると、そうは実行されません。親のコンストラクタがこのジョブを実行することを期待しています。 - -なので、もし独自のコンスタクタを作っている場合には、`super` を呼ばないといけません。なぜなら、そうしないとそれを参照する `this` を持つオブジェクトは生成されないからです。 結果、エラーになるでしょう。 - -`Rabbit` を動作させるために、`this` を使う前に `super()` を呼ぶ必要があります。: - -```js run -class Animal { - - constructor(name) { - this.speed = 0; - this.name = name; - } - - // ... -} - -class Rabbit extends Animal { - - constructor(name, earLength) { -*!* - super(name); -*/!* - this.earLength = earLength; - } - - // ... -} - -*!* -// 今は問題ありませんn -let rabbit = new Rabbit("White Rabbit", 10); -alert(rabbit.name); // White Rabbit -alert(rabbit.earLength); // 10 -*/!* -``` - - -## Super: internals, [[HomeObject]] - -`super` の内部をもう少し深く見てみましょう。ここで面白いことがいくつか見られます。 - -まず最初に、今まで私たちが学んだすべてのことだけでは、 `super` が動作するのは不可能です。 - -たしかに技術的にどのように動くのでしょうか?オブジェクトメソッドが実行されるとき、`this` として現在のオブジェクトを取ります。もし `super.method()` を呼び出す場合、どうやって `method` を取得するでしょう?当然ながら、我々は現在のオブジェクトのプロトタイプから `method` を取る必要があります。技術的に我々(またはJavaScriptエンジンは)どうやってそれをするのでしょうか? - -恐らく、`this.__proto__.method` とすることで `this` の `[[Prototype]]` からメソッドを取得できる?残念ながらそれは動作しません。 - -それにトライしてみましょう。簡単にするために、クラスなしで単純なオブジェクトを使用します。 - -ここでは、`rabbit.eat()` は親オブジェクトの `animal.eat()` メソッドを呼び出す必要があります。: - -```js run -let animal = { - name: "Animal", - eat() { - alert(this.name + " eats."); - } -}; - -let rabbit = { - __proto__: animal, - name: "Rabbit", - eat() { -*!* - // これがおそらく super.eat() が動作する方法です - this.__proto__.eat.call(this); // (*) -*/!* - } -}; - -rabbit.eat(); // Rabbit eats. -``` - -行 `(*)` でプロトタイプ(`animal`) から `eat` を取り、現在のオブジェクトコンテキストでそれを呼び出します。 -`.call(this)` はここでは重要であることに注意してください。なぜなら、シンプルな `this.__proto__.eat()` は現在のオブジェクトではなくプロトタイプのコンテキストで親の `eat` を実行するためです。 - -また、上のコードは実際に期待通り動作します: 正しい `alert` になります。 - -今度はもう1つのオブジェクトをチェーンに追加しましょう。 私たちは物事がどのように壊れるかを見ていきます: - -```js run -let animal = { - name: "Animal", - eat() { - alert(this.name + " eats."); - } -}; - -let rabbit = { - __proto__: animal, - eat() { - // ...bounce around rabbit-style - // 親 (animal) メソッドを呼び出す - this.__proto__.eat.call(this); // (*) - } -}; - -let longEar = { - __proto__: rabbit, - eat() { - // ...do something with long ears - // 親 (rabbit) メソッドを呼び出す - this.__proto__.eat.call(this); // (**) - } -}; - -*!* -longEar.eat(); // Error: 最大呼び出しスタックサイズを超えました -*/!* -``` - -コードはこれ以上動作しません! `longEar.ear()` を呼び出そうとするとエラーになります。 - -これは明白ではないかもしれませんが、もし `longEar.eat()` 呼び出しのトレースをすると、それがなぜかがわかります。行 `(*)` と `(**)` は共に、`this` の値は現在のオブジェクト (`longEar`) です。それが肝心です: すべてのオブジェクトメソッドはプロトタイプなどではなく、現在のオブジェクトを `this` として取得します。 - -したがって、行 `(*)` と `(**)` は共に、`this.__proto__` の値は全く同じで、`rabbit` です。それらは両方とも、無限ループで `rabbit.eat` を呼んでいます。 - -これは何が起きているかを示す図です。: - -![](this-super-loop.png) - -1. `longEar.eat()` の中で、行 `(**)` は `this=longEar` となる `rabbit.eat` を呼び出します。 - ```js - // longEar.eat() の中では this = longEar です - this.__proto__.eat.call(this) // (**) - // なので次のようになります - longEar.__proto__.eat.call(this) - // つまり呼ばれるのは - rabbit.eat.call(this); - ``` -2. 次に `rabbit.eat` の 行 `(*)` で、チェーンの中でより高次へ呼び出しを渡したいですが、`this=longEar` なので、 `this=__prto__.eat` は再び `rabbit.eat` です! - ```js - // rabbit.eat() の中でも this = longEar です - this.__proto__.eat.call(this) // (*) - // なので次のようになります - longEar.__proto__.eat.call(this) - // なので (再び) - rabbit.eat.call(this); - ``` - -3. ...したがって、それ以上高次へ登ることができないので、`rabbit.eat` はエンドレスで自身を呼び出します。 - -`this` だけを使ってこの問題を解くことはできません。 - -### `[[HomeObject]]` - -その解決策を提供するため、JavaScriptはもう1つ関数のための特別な内部プロパティを追加しています: `[[HomeObject]]` です。 - -**関数がクラスまたはオブジェクトメソッドとして指定されたとき、その `[[HomeObject]]` プロパティはそのオブジェクトになります。** - -メソッドはそれらのオブジェクトを覚えているため、これは実際には "バインドされていない" 関数の考え方に反しています。また、`[[HomeObject]]` は変更することはできないため、このバインドは永遠です。なので、これは言語上の非常に重要な変更です。 - -しかし、この変更は安全です。`[[HomeObject]]` は プロトタイプを解決するために、`super` の中で親メソッドを呼び出すためだけに使われます。従って、互換性を損ねることはありません。 - -`super` がどのように動くのか見てみましょう -- 平易なオブジェクトを使って再び。: - -```js run -let animal = { - name: "Animal", - eat() { // [[HomeObject]] == animal - alert(this.name + " eats."); - } -}; - -let rabbit = { - __proto__: animal, - name: "Rabbit", - eat() { // [[HomeObject]] == rabbit - super.eat(); - } -}; - -let longEar = { - __proto__: rabbit, - name: "Long Ear", - eat() { // [[HomeObject]] == longEar - super.eat(); - } -}; - -*!* -longEar.eat(); // Long Ear eats. -*/!* -``` - -すべてのメソッドは内部の `[[HomeObject]]` プロパティで、そのオブジェクトを覚えています。そして `super` は親のプロトタイプの解決のために `[[HomeObject]]` を使います。 - -`[[HomeObject]]` はクラスと単純なオブジェクト両方で定義されたメソッドのために定義されています。しかし、オブジェクトの場合、メソッドは指定された方法で正確に指定されなければなりません。: `"method: function()"` ではなく `method()` として指定する必要があります。 - -下の例では、上の例との比較のために非メソッド構文を使っています。`[[HomeObject]]` プロパティはセットされず、継承は動作しません。: - -```js run -let animal = { - eat: function() { // 短縮構文: eat() {...} にする必要があります - // ... - } -}; - -let rabbit = { - __proto__: animal, - eat: function() { - super.eat(); - } -}; - -*!* -rabbit.eat(); // super 呼び出しエラー([[HomeObject]] が無いため) -*/!* -``` - -## 静的メソッドと継承 - -`class` 構文は静的なプロパティに対しても継承をサポートしています。 - -例: - -```js run -class Animal { - - constructor(name, speed) { - this.speed = speed; - this.name = name; - } - - run(speed = 0) { - this.speed += speed; - alert(`${this.name} runs with speed ${this.speed}.`); - } - - static compare(animalA, animalB) { - return animalA.speed - animalB.speed; - } - -} - -// Animal から継承 -class Rabbit extends Animal { - hide() { - alert(`${this.name} hides!`); - } -} - -let rabbits = [ - new Rabbit("White Rabbit", 10), - new Rabbit("Black Rabbit", 5) -]; - -rabbits.sort(Rabbit.compare); - -rabbits[0].run(); // Black Rabbit runs with speed 5. -``` - -これで、継承された `Animal.compare` が呼び出されると想定される `Rabbit.compare` を呼ぶことができます。 - -どのように動くのでしょう?繰り返しますが、プロトタイプを使用します。 すでに推測したように、`extends` は `Rabbit` に `Animal` への `[[Prototype]]` 参照を与えます。 - -![](animal-rabbit-static.png) - -従って、`Rabbit` 関数は `Animal` 関数から継承します。そして `Animal` 関数は通常 `Function.prototype` を参照する `[[Prototype]]` を持っています。なぜならそれは何も `extend` していないからです。 - -ここで、それを確認してみましょう。: - -```js run -class Animal {} -class Rabbit extends Animal {} - -// 静的プロパティとメソッドの場合 -alert(Rabbit.__proto__ == Animal); // true - -// 次のステップは Function.prototype です -alert(Animal.__proto__ == Function.prototype); // true - -// オブジェクトメソッドの "通常の" プロトタイプチェーンに加えて -alert(Rabbit.prototype.__proto__ === Animal.prototype); // true -``` - -このように `Rabbit` は `Animal` のすべての静的メソッドへアクセスです。 - -### 組み込みで、静的継承はありません - -組み込みのクラスはこのような静的な `[[Prototype]]` 参照は持っていないことに注意してください。例えば、`Object` は `Object.defineProperty`, `Object.keys` やその他を持っていますが、`Array` や `Date` などはそれらを継承しません。 - -これは `Date` や `Object` の構造を示す図です。: - -![](object-date-inheritance.png) - -`Date` と `Object` の間のリンクはないことに注意してください。`Object` と `Date` は独立して存在します。`Date.prototype` は `Object.prototype` から継承しますが、それだけです。 - -このような差異は歴史的な理由で存在します。: JavaScript言語の幕開けにクラス構文と静的メソッドを継承することは考えられませんでした。 - -## ネイティブは拡張可能です - -Array, Map やその他のような組み込みのクラスもまた拡張可能です。 - -例えば、ここでは `PowerArray` はネイティブの `Array` から継承しています。: - -```js run -// メソッドを追加する(もっと追加することもできます) -class PowerArray extends Array { - isEmpty() { - return this.length == 0; - } -} - -let arr = new PowerArray(1, 2, 5, 10, 50); -alert(arr.isEmpty()); // false - -let filteredArr = arr.filter(item => item >= 10); -alert(filteredArr); // 10, 50 -alert(filteredArr.isEmpty()); // false -``` - -1つとても興味深いことに留意してください。`filter`, `map` やその他組み込みのメソッドは -- 正確に継承された型の新しいオブジェクトを返します。 それらはそうするために `constructor`プロパティに依存しています。 - -上の例です, -```js -arr.constructor === PowerArray -``` - -なので、`arr.filter()` が呼ばれた時、それは内部で `new PowerArray` と同じ結果の新しい配列が作成されます。 そして、呼び出しのチェーンの下でさらに使い続けることができます。 - -さらにその振る舞いをカスタマイズすることもできます。静的な getter `Symbol.species` が存在する場合、そのようなケースで使うためにコンストラクタを返します。 - -例えば、ここでは `Symbol.species` によって、`map`, `filter` のような組み込みメソッドは "通常の" 配列を返します。: - -```js run -class PowerArray extends Array { - isEmpty() { - return this.length == 0; - } - -*!* - // 組み込みのメソッドはこれをコンストラクタとして使います - static get [Symbol.species]() { - return Array; - } -*/!* -} - -let arr = new PowerArray(1, 2, 5, 10, 50); -alert(arr.isEmpty()); // false - -// fileter はコンストラクタとして arr.constructor[Symbol.species] を使って新しい配列を作ります。 -let filteredArr = arr.filter(item => item >= 10); - -*!* -// filteredArr は PowerArray ではなく Array です -*/!* -alert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty は関数ではありません -``` - -より高度な手段として使用し、不要な場合には結果の値から拡張機能を取り除くことができます。 あるいは、さらに拡張することもできます。 diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object.png b/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object.png deleted file mode 100644 index c5d712632f..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object@2x.png deleted file mode 100644 index edc4e841ec..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-array-object@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal.png b/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal.png deleted file mode 100644 index 70708c2848..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal@2x.png deleted file mode 100644 index 0db1301815..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-animal@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-run-animal.png b/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-run-animal.png deleted file mode 100644 index 387975a9c7..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-run-animal.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-run-animal@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-run-animal@2x.png deleted file mode 100644 index ca73135933..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/class-inheritance-rabbit-run-animal@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance.png b/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance.png deleted file mode 100644 index 542a0c9fa9..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance@2x.png deleted file mode 100644 index 21485062a8..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/object-date-inheritance@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop.png b/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop.png deleted file mode 100644 index 637d179398..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop@2x.png b/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop@2x.png deleted file mode 100644 index af7b443bcb..0000000000 Binary files a/1-js/07-object-oriented-programming/10-class-inheritance/this-super-loop@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/11-instanceof/article.md b/1-js/07-object-oriented-programming/11-instanceof/article.md deleted file mode 100644 index 99e7a7a490..0000000000 --- a/1-js/07-object-oriented-programming/11-instanceof/article.md +++ /dev/null @@ -1,213 +0,0 @@ -# クラスのチェック: "instanceof" - -`instanceof` 演算子でオブジェクトが特定のクラスに属しているのかを確認することができます。また、継承も考慮に入れます。 - -このようなチェックが必要なケースは多々あるかもしれません。ここでは、その型に応じて引数を別々に扱う *多形(ポリモーフィック)* 関数を構築するために使用します。 - -[cut] - -## instanceof 演算子 - -構文は次の通りです: -```js -obj instanceof Class -``` - -それは `obj` が `Class` (または、それを継承しているクラス)に属している場合に `true` を返します。 - -例: - -```js run -class Rabbit {} -let rabbit = new Rabbit(); - -// Rabbit クラスのオブジェクト? -*!* -alert( rabbit instanceof Rabbit ); // true -*/!* -``` - -それはコンストラクタ関数でも動作します。: - -```js run -*!* -// class の代わり -function Rabbit() {} -*/!* - -alert( new Rabbit() instanceof Rabbit ); // true -``` - -...また `Array` のような組み込みクラスでも動作します。: - -```js run -let arr = [1, 2, 3]; -alert( arr instanceof Array ); // true -alert( arr instanceof Object ); // true -``` - -`arr` は `Object` クラスにも属していることに留意してください。`Array` はプロトタイプ的に `Object` を継承しているためです。 - -`instanceof` 演算子は確認のためにプロトタイプチェーンを検査し、それは静的メソッド `Symbo.hasInstance` を使って微調整することが可能です。 - -`obj instanceof Class` のアルゴリズムはおおまかに次のように動作します。: - -1. もし静的メソッド `Symbol.hasInstance` があれば、それを使います。このようになります。: - - ```js run - // canEat は animal と仮定 - class Animal { - static [Symbol.hasInstance](obj) { - if (obj.canEat) return true; - } - } - - let obj = { canEat: true }; - alert(obj instanceof Animal); // true: Animal[Symbol.hasInstance](obj)が呼ばれます - ``` - -2. ほとんどのクラスは `Symbol.hasInstance` を持っていません。このケースでは、`Class.prototype` が `obj` のプロトタイプチェーンうちの1つと等しいかをチェックします。 - - 言い換えると、以下のような比較を行います: - ```js - obj.__proto__ == Class.prototype - obj.__proto__.__proto__ == Class.prototype - obj.__proto__.__proto__.__proto__ == Class.prototype - ... - ``` - - 上の例では、`Rabbit.prototype == rabbit.__proto__` なので、すぐに回答が得られます。 - - 継承のケースでは、`rabbit` も同様に親クラスのインスタンスです。: - - ```js run - class Animal {} - class Rabbit extends Animal {} - - let rabbit = new Rabbit(); - *!* - alert(rabbit instanceof Animal); // true - */!* - // rabbit.__proto__ == Rabbit.prototype - // rabbit.__proto__.__proto__ == Animal.prototype (match!) - ``` - -これは、`rabbit instanceof Animal` と `Animal.prototype` を比較したものです。: - -![](instanceof.png) - -ところで、[objA.isPrototypeOf(objB)](mdn:js/object/isPrototypeOf) というメソッドもあります。それは `objA` が `objB` のプロトタイプチェーンのどこかにあれば `true` を返します。なので、`obj instanceof Class` のテストは `Class.prototype.isPrototypeOf(obj)` と言い換えることができます。 - -面白いことに、`Class` コンストラクタ自身はチェックには参加しません! プロトタイプと `Class.prototype`のチェーンだけです。 - -これは `prototype` が変更されたときに興味深い結果につながります。 - -このように: - -```js run -function Rabbit() {} -let rabbit = new Rabbit(); - -// prototype を変更します -Rabbit.prototype = {}; - -// ...もう rabbit ではありません -*!* -alert( rabbit instanceof Rabbit ); // false -*/!* -``` - -これが `prototype` の変更を避ける理由の1つです。安全を保つためです。 - -## おまけ: 型のための Object toString - -私たちは通常の文字列は `[object Object]` という文字列に変換されることをすでに知っています。: - -```js run -let obj = {}; - -alert(obj); // [object Object] -alert(obj.toString()); // 同じ -``` - -これが `toString` の実装です。しかし、実際にはそれよりもはるかに強力な `toString` を作る隠れた機能があります。それを拡張させて `typeof` または `instanceof` の代替として利用することができます。 - -奇妙に聞こえますか?たしかに。分かりやすく説明しましょう。 - -[仕様(specification)](https://tc39.github.io/ecma262/#sec-object.prototype.tostring)によって、組み込みの `toString` はオブジェクトから抽出し、任意の値のコンテキストで実行することができます。そして、その結果はその値に依存します。 - -- 数値の場合、それは `[object Number]` になります。 -- 真偽値の場合、`[object Boolean]` になります。 -- `null` の場合: `[object Null]` -- `undefined` の場合: `[object Undefined]` -- 配列の場合: `[object Array]` -- ...など (カスタマイズ可能). - -デモを見てみましょう: - -```js run -// 使いやすくするために toString メソッドを変数にコピー -let objectToString = Object.prototype.toString; - -// これの型はなに? -let arr = []; - -alert( objectToString.call(arr) ); // [object Array] -``` - -ここでは、コンテキスト `this=arr` で関数 `objectToString` を実行するため、チャプター [デコレータと転送, call/apply](info:call-apply-decorators) で説明した [call](mdn:js/function/call) を使いました。 - -内部的には、`toString` アルゴリズムは `this` を検査し、対応する結果を返します。ほかの例です。: - -```js run -let s = Object.prototype.toString; - -alert( s.call(123) ); // [object Number] -alert( s.call(null) ); // [object Null] -alert( s.call(alert) ); // [object Function] -``` - -### Symbol.toStringTag - -Object `toString` の振る舞いは特別なオブジェクトプロパテ `Symbol.toStringTag` を使ってカスタマイズすることができます。 - -例: - -```js run -let user = { - [Symbol.toStringTag]: 'User' -}; - -alert( {}.toString.call(user) ); // [object User] -``` - -ほとんどの環境固有のオブジェクトには、そのようなプロパティがあります。 ブラウザ固有の例はほとんどありません。: - -```js run -// 環境固有のオブジェクトとクラスのtoStringTag: -alert( window[Symbol.toStringTag]); // window -alert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest - -alert( {}.toString.call(window) ); // [object Window] -alert( {}.toString.call(new XMLHttpRequest()) ); // [object XMLHttpRequest] -``` - -ご覧の通り、結果は正確に `Symbol.toStringTag` (存在する場合)で、`[object ...]` の中にラップされています。 - -最終的には、プリミティブなデータ型だけでなく、組み込みオブジェクトのためにも機能し、カスタマイズすることもできる "強化された typeof" があります。 - -これは、型を文字列として取得するだけでなく、チェックするために、組み込みオブジェクトに対して `instanceof` の代わりに使用できます。 - -## サマリ - -私たちが知っている型チェックメソッドについて再確認しましょう: - -| | 対象 | 戻り値 | -|---------------|-------------|---------------| -| `typeof` | プリミティブ | 文字列 | -| `{}.toString` | プリミティブ, 組み込みオブジェクト, `Symbol.toStringTag` をもつオブジェクト | 文字列 | -| `instanceof` | オブジェクト | true/false | - -ご覧のように、`{}.toString` は技術的には "より高度な" `typeof` です。 - -そして、`instanceof` 演算子は、クラス階層を扱っていて継承を考慮したクラスのチェックをしたい場合に本当に輝きます。 diff --git a/1-js/07-object-oriented-programming/11-instanceof/instanceof.png b/1-js/07-object-oriented-programming/11-instanceof/instanceof.png deleted file mode 100644 index 85aa9a55f3..0000000000 Binary files a/1-js/07-object-oriented-programming/11-instanceof/instanceof.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/11-instanceof/instanceof@2x.png b/1-js/07-object-oriented-programming/11-instanceof/instanceof@2x.png deleted file mode 100644 index fba7712207..0000000000 Binary files a/1-js/07-object-oriented-programming/11-instanceof/instanceof@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/13-mixins/article.md b/1-js/07-object-oriented-programming/13-mixins/article.md deleted file mode 100644 index d6dc647a63..0000000000 --- a/1-js/07-object-oriented-programming/13-mixins/article.md +++ /dev/null @@ -1,205 +0,0 @@ -# ミックスイン - -JavaScriptでは、単一のオブジェクトからのみ継承できます。オブジェクトの `[[Prototype]]` は1つしかありません。そしてクラスは1つの他のクラスだけを拡張することができます。 - -しかし、それを制限と感じる場合があります。例えば、`StreetSweeper` と `Bycicle`c というクラスを持っていて、`StreetSweepingBycicle` を作りたい場合などです。 - -あるいは `Renderer` クラスとイベントハンドラを実装する `EventEmitter` クラスを持っていて、テンプレートとイベントの発火を利用できるページを作るために、`Page` クラスで一緒にそれらの機能性をマージしたい場合があります。 - -ここでは、それを助ける "mixins(ミックスイン)" と呼ばれるコンセプトがあります。 - -Wikipedis の定義によると、[mixin](https://en.wikipedia.org/wiki/Mixin) は他のクラスの親クラスでないが、他のクラスで使用するためのメソッドを含むクラスです。 - -つまり、*mixin* は特定の振る舞いを実装したメソッドを提供しますが、単独では使わず、別のクラスの振る舞いを追加するために使います。 - - -## mixin のサンプル - -JavaScriptで mixin を作る最もシンプルな方法は、役立つメソッドをもつオブジェクトを作ることです。そのすることで、それらを簡単にどのクラスのプロトタイプにもマージすることができます。 - -例えば、ここでは mixin `sayHiMixin` は `User` のためのいくつかの "スピーチ" を追加するために使われます。: - -```js run -*!* -// mixin -*/!* -let sayHiMixin = { - sayHi() { - alert("Hello " + this.name); - }, - sayBye() { - alert("Bye " + this.name); - } -}; - -*!* -// 使い方: -*/!* -class User { - constructor(name) { - this.name = name; - } -} - -// メソッドをコピー -Object.assign(User.prototype, sayHiMixin); - -// これで User は sayHi できます -new User("Dude").sayHi(); // Hi Dude! -``` - -これは継承ではなく、単純なメソッドのコピーです。従って、`User` は他のクラスを拡張することができ、さらに以下のように追加のメソッドをミックスインするとして含めることができます: - -```js -class User extends Person { - // ... -} - -Object.assign(User.prototype, sayHiMixin); -``` - -ミックスインは自身の内部で継承を活用することもできます。。 - -例えば、ここでは `sayHiMixin` は `sayMixin` を継承しています。: - -```js run -let sayMixin = { - say(phrase) { - alert(phrase); - } -}; - -let sayHiMixin = { - __proto__: sayMixin, // (またはここで prototype を設定するのに Object.create が使えます) - - sayHi() { - *!* - // 親のメソッド呼び出し - */!* - super.say("Hello " + this.name); - }, - sayBye() { - super.say("Bye " + this.name); - } -}; - -class User { - constructor(name) { - this.name = name; - } -} - -// メソッドをコピー -Object.assign(User.prototype, sayHiMixin); - -// これで User は sayHi できます -new User("Dude").sayHi(); // Hello Dude! -``` - -`sayHiMixin` からの親メソッド `super.say()` の呼び出しは、クラスではなくそのミックスインのプロトタイプの中のメソッドを探すことに注意してください。 - -![](mixin-inheritance.png) - -これは `sayHiMixin` のメソッドに `[[HomeObject]]` がセットされているからです。そのため、`super` は実際には `sayHiMixin.__proto__`を意味し、`User.__proto__`を意味しません。 - -## イベントMixin - -さて、実践のためのミックスインを作ってみましょう。 - -多くのオブジェクトの重要な特徴はイベントを扱うことです。 - -つまり: オブジェクトには、何か重要な事が起きたときに "イベントを生成する" メソッドが必要であり、他のオブジェクトはこのようなイベントを "リッスン" できる必要があります。 - -イベントには名前が必要であり、必要に応じて追加データをバンドルする必要があります。 - -例えば、オブジェクト `user` は訪問者がログインするときに `"login"` というイベントを生成することができます。そして、別オブジェクト `calendar` はログインした人のカレンダーをロードするために、このようなイベントを受けりたいかもしれません。 - -あるいは、`menu` はメニューアイテムが選択されたときに `"select"` イベントを生成することができ、他のオブジェクトはその情報を取得したりイベントに反応したいかもしれません。 - -イベントは、それを望む人と "情報を共有する" 方法です。 それらはどのクラスでも役に立ちますので、そのためのミックスインを作ってみましょう: - -```js run -let eventMixin = { - /** - * イベントの購読, 使い方: - * menu.on('select', function(item) { ... } - */ - on(eventName, handler) { - if (!this._eventHandlers) this._eventHandlers = {}; - if (!this._eventHandlers[eventName]) { - this._eventHandlers[eventName] = []; - } - this._eventHandlers[eventName].push(handler); - }, - - /** - * 購読のキャンセル 使い方: - * menu.off('select', handler) - */ - off(eventName, handler) { - let handlers = this._eventHandlers && this._eventHandlers[eventName]; - if (!handlers) return; - for(let i = 0; i < handlers.length; i++) { - if (handlers[i] == handler) { - handlers.splice(i--, 1); - } - } - }, - - /** - * イベントを生成してデータをアタッチ - * this.trigger('select', data1, data2); - */ - trigger(eventName, ...args) { - if (!this._eventHandlers || !this._eventHandlers[eventName]) { - return; // イベントに対応するハンドラがない場合 - } - - // ハンドラ呼び出し - this._eventHandlers[eventName].forEach(handler => handler.apply(this, args)); - } -}; -``` - -ここでは3つのメソッドがあります: - -1. `.on(eventName, handler)` -- その名前のイベントが発生した時に実行するための関数 `handler` を割り当てます。ハンドラは `_eventHandlers` プロパティの中に格納されます。 -2. `.off(eventName, handler)` -- ハンドラリストから関数を削除します。 -3. `.trigger(eventName, ...args)` -- イベントを生成します: すべての割り当てられたハンドラが呼び出され、`args` がそれらの引数として渡されます。 - -使い方: - -```js run -// クラスを作成 -class Menu { - choose(value) { - this.trigger("select", value); - } -} -// mixin を追加 -Object.assign(Menu.prototype, eventMixin); - -let menu = new Menu(); - -// 選択時にハンドラを呼び出し -*!* -menu.on("select", value => alert("Value selected: " + value)); -*/!* - -// イベントのトリガ => Value selected: 123 を表示 -menu.choose("123"); // 選択された値 -``` - -これで、もしユーザ選択に反応するためのコードがある場合、`menu.on(...)` でバインドすることができます。 - -そして、`eventMixin` は継承のチェーンを邪魔することなく、我々が望むだけのクラスに対してこのような振る舞いを追加することができます。 - -## サマリ - -*Mixin(ミックスイン)* -- は一般的なオブジェクト指向プログラミングの言葉です: 他のクラスのためのメソッドを含むクラスです。 - -python のようないくつかの他の言語は、多重継承を使いミックスインを作成することができます。JavaScriptでは多重継承をサポートしていませんが、プロトタイプにそれらをコピーすることでミックスインを実装することができます。 - -上で見てきたように、イベントハンドリングのような複数の振る舞いによってクラスを拡張する方法としてミックスインが利用できます。 - -時折、ミックスインがネイティブのクラスメソッドを上書きすると、矛盾する点になることがあります。なので、一般的にはこのような可能性を最小化するために、ミックスインの名付けについてよく考える必要があります。 diff --git a/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance.png b/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance.png deleted file mode 100644 index 7cc655036f..0000000000 Binary files a/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance@2x.png b/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance@2x.png deleted file mode 100644 index f53ecf68fd..0000000000 Binary files a/1-js/07-object-oriented-programming/13-mixins/mixin-inheritance@2x.png and /dev/null differ diff --git a/1-js/07-object-oriented-programming/index.md b/1-js/07-object-oriented-programming/index.md deleted file mode 100644 index c6d08b0936..0000000000 --- a/1-js/07-object-oriented-programming/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# オブジェクト、クラス、継承 - -このセクションでは、オブジェクトに戻りさらに深く学びます。 diff --git a/1-js/07-object-properties/01-property-descriptors/article.md b/1-js/07-object-properties/01-property-descriptors/article.md new file mode 100644 index 0000000000..9647d82679 --- /dev/null +++ b/1-js/07-object-properties/01-property-descriptors/article.md @@ -0,0 +1,349 @@ + +# プロパティフラグとディスクリプタ + +ご存知の通り、オブジェクトはプロパティを格納できます。 + +これまで、プロパティは単純な "key-value" ペアでしたが、実際にはオブジェクトプロパティはより柔軟で強力なものです。 + +この章では、追加の設定オプションについて説明し、次の章では、それらを見えない形、getter/setter 関数にする方法について見ていきます。 + +## プロパティフラグ + +オブジェクトプロパティには、 **`value`** の他に、3つの特別な属性があります("フラグ" と呼ばれています)。 + +- **`writable`** -- `true` の場合、変更可能です。それ以外の場合は読み取り専用です。 +- **`enumerable`** -- `true` の場合、ループで列挙されます。それ以外の場合は列挙されません。 +- **`configurable`** -- `true` の場合、プロパティを削除したり属性の変更ができます。 + +一般的にこれらは姿を見せることがないため、まだ見ていませんでした。"通常の方法" でプロパティを作成するとき、これらはすべて `true` です。が、いつでもそれを変更することができます。 + +まず、フラグを取得する方法を見てみましょう。 + +メソッド [Object.getOwnPropertyDescriptor](mdn:js/Object/getOwnPropertyDescriptor) で、プロパティの *完全な* 情報を参照することができます。 + +構文は次の通りです: +```js +let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName); +``` + +`obj` +: 情報を取得するオブジェクトです。 + +`propertyName` +: プロパティ名です。 + +返却値はいわゆる "プロパティディスクリプタ" オブジェクトと呼ばれます。: それは値とすべてのフラグを含んでいます。 + +例: + +```js run +let user = { + name: "John" +}; + +let descriptor = Object.getOwnPropertyDescriptor(user, 'name'); + +alert( JSON.stringify(descriptor, null, 2 ) ); +/* プロパティディスクリプタ: +{ + "value": "John", + "writable": true, + "enumerable": true, + "configurable": true +} +*/ +``` + +[Object.defineProperty](mdn:js/Object/defineProperty) でフラグの変更ができます。 + +構文: + +```js +Object.defineProperty(obj, propertyName, descriptor) +``` + +`obj`, `propertyName` +: 処理するオブジェクトとプロパティです。 + +`descriptor` +: 適用するプロパティディスクリプタです。 + +プロパティが存在する場合、`defineProperty` はそのフラグを更新します。存在しない場合は指定された値とフラグでプロパティを作ります。その場合に、もしフラグが指定されていなければ `false` とみなされます。 + +例えば、ここではプロパティ `name` はすべて `false` のフラグで作られます。: + +```js run +let user = {}; + +*!* +Object.defineProperty(user, "name", { + value: "John" +}); +*/!* + +let descriptor = Object.getOwnPropertyDescriptor(user, 'name'); + +alert( JSON.stringify(descriptor, null, 2 ) ); +/* +{ + "value": "John", +*!* + "writable": false, + "enumerable": false, + "configurable": false +*/!* +} + */ +``` + +"通常の方法で" 作成された `user.name` と上記を比較してください。今すべてのフラグは `false` です。このようにしたくなければ、`descriptor` で `true` をセットするのがよいでしょう。 + +では、例を使ってフラグの影響を見てみましょう。 + +## 書き込み不可(Non-writable) + +`writable` フラグを変更して `user.name` を書き込み不可(再代入不可)にしてみましょう: + +```js run +let user = { + name: "John" +}; + +Object.defineProperty(user, "name", { +*!* + writable: false +*/!* +}); + +*!* +user.name = "Pete"; // Error: Cannot assign to read only property 'name'... +*/!* +``` + +これで、`defineProperty` で上書きをしない限りは、誰も user.name を変えることはできません。 + +```smart header="strict mode の場合のみエラーが表示されます" +非 strict mode の場合、書き込み不可プロパティへの書き込みをしてもエラーは発生しません。ですが、操作は依然として成功はしません。非 strict 下では、フラグ違反の操作は単に無視されます。 +``` + +これは先程と同じ例ですが、スクラッチでプロパティを作成します: + +```js run +let user = { }; + +Object.defineProperty(user, "name", { +*!* + value: "Pete", + // 新しいプロパティに対して、true のものは明示的に列挙する必要があります + enumerable: true, + configurable: true +*/!* +}); + +alert(user.name); // Pete +user.name = "Alice"; // Error +``` + +## 列挙可能でない(Non-enumerable) + +カスタムの `toString` を `user` に追加しましょう。 + +通常、オブジェクトが持つ組み込みの `toString` は列挙可能ではありません。それは `for..in` では表示されません。しかし私たちが自身の `toString` を追加した場合、デフォルトではこのように `for..in` で表示されます。: + +```js run +let user = { + name: "John", + toString() { + return this.name; + } +}; + +// デフォルトでは、両方のプロパティは列挙されます: +for (let key in user) alert(key); // name, toString +``` + +列挙されたくなければ、`enumerable:false` をセットします。すると、組み込みの関数同様、`for..in` ループで列挙されなくなります。: + +```js run +let user = { + name: "John", + toString() { + return this.name; + } +}; + +Object.defineProperty(user, "toString", { +*!* + enumerable: false +*/!* +}); + +*!* +// これで toString は消えました: +*/!* +for (let key in user) alert(key); // name +``` + +列挙可能でないプロパティは `Object.keys` からも除外されます。: + +```js +alert(Object.keys(user)); // name +``` + +## 変更できない(Non-configurable) + +組み込みオブジェクトやプロパティに対しては、変更不可フラグ(`configurable:false`)がプリセットされることがあります。 + +変更できないプロパティは削除したり変更することができません。 + +例えば、`Math.PI` は書き込み不可で、列挙不可であり、変更不可です: + +```js run +let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI'); + +alert( JSON.stringify(descriptor, null, 2 ) ); +/* +{ + "value": 3.141592653589793, + "writable": false, + "enumerable": false, + "configurable": false +} +*/ +``` +したがって、プログラマは `Math.PI` の値を変えることも上書きすることもできません。 + +```js run +Math.PI = 3; // Error + +// delete Math.PI もまた動作しません +``` + +`Math.PI` を再度 `writable` にすることもできません。: + +```js run +// Error, configurable: false なので +Object.defineProperty(Math, "PI", { writable: true }); +``` + +`Math.PI` についてはは何もできません。 + +変更不可なプロパティの作成は一方通行であり、それを `defineProperty` 戻すことはできません。 + +**注意: `configurable: false` はプロパティフラグの変更や削除を禁止しますが、値を変更することは可能です** + +ここでは、 `user.name` は変更不可ですが、依然として変更はできます(書き込み可なので): + +```js run +let user = { + name: "John" +}; + +Object.defineProperty(user, "name", { + configurable: false +}); + +user.name = "Pete"; // 動作します +delete user.name; // Error +``` + +また、以下は組み込みの`Math.PI` のように `user.name` を "永遠に封印された定数" にしています。 + +```js run +let user = { + name: "John" +}; + +Object.defineProperty(user, "name", { + writable: false, + configurable: false +}); + +// user.name とフラグは変更できません +// これらはすべて動作しません: +user.name = "Pete"; +delete user.name; +Object.defineProperty(user, "name", { value: "Pete" }); +``` + +```smart header="唯一可能な属性変更: writable true -> false" +フラグ変更に関する小さな例外があります。 + +変更不可プロパティに対して、`writable: true` を `false` に変更し、値の変更を防ぐことができます。しかし、その逆はできません。 +``` + +## Object.defineProperties + +一度に多くのプロパティが定義できるメソッド [Object.defineProperties(obj, descriptors)](mdn:js/Object/defineProperties)もあります。 + +構文は次の通りです: + +```js +Object.defineProperties(obj, { + prop1: descriptor1, + prop2: descriptor2 + // ... +}); +``` + +例えば: + +```js +Object.defineProperties(user, { + name: { value: "John", writable: false }, + surname: { value: "Smith", writable: false }, + // ... +}); +``` + +なので、一度に多くのプロパティをセットできます。 + +## Object.getOwnPropertyDescriptors + +一度にすべてのプロパティのディスクリプタを取得するには、[Object.getOwnPropertyDescriptors(obj)](mdn:js/Object/getOwnPropertyDescriptors) が使用できます。 + +`Object.defineProperties` と合わせて、"フラグを意識して" オブジェクトをクローンする方法として使うことができます。: + +```js +let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj)); +``` + +通常、オブジェクトをクローンするとき、次のようにプロパティをコピーするために代入を使います。: + +```js +for (let key in user) { + clone[key] = user[key] +} +``` + +...ですが、これはフラグはコピーしません。そのため、"より良い" クローンを望むなら、 `Object.defineProperties` が好まれます。 + +もう1つの違いは、`for..in` はシンボルプロパティを無視しますが、`Object.getOwnPropertyDescriptors` はシンボリックなものを含む *すべての* プロパティディスクリプタを返します。 + +## グローバルにオブジェクトを隠す + +プロパティディスクリプタは個々のプロパティのレベルで動作します。 + +そこには、オブジェクト *全体* へのアクセスを制限するメソッドもあります。: + +[Object.preventExtensions(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions) +: オブジェクトにプロパティを追加するのを禁止します。 + +[Object.seal(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal) +: プロパティの追加、削除を禁止し、既存のすべてのプロパティに `configurable: false` をセットします。 + +[Object.freeze(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) +: プロパティの追加、削除、変更を禁止し、既存のすべてのプロパティに `configurable: false, writable: false` をセットします。 + +また、それらを確認する方法もあります: + +[Object.isExtensible(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible) +: プロパティの追加が禁止されている場合に `false` を返します。それ以外は `true` です。 + +[Object.isSealed(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed) +: プロパティの追加、削除が禁止されており、すべての既存のプロパティが `configurable: false` を持っている場合に `true` を返します。 + +[Object.isFrozen(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen) +: プロパティの追加、削除、変更が禁止されており、すべての現在のプロパティが `configurable: false, writable: false` の場合に `true` を返します。 + +これらのメソッドは実際にはめったに使われません。 diff --git a/1-js/07-object-properties/02-property-accessors/article.md b/1-js/07-object-properties/02-property-accessors/article.md new file mode 100644 index 0000000000..7d83b91544 --- /dev/null +++ b/1-js/07-object-properties/02-property-accessors/article.md @@ -0,0 +1,244 @@ + +# プロパティ getters と setters + +オブジェクトプロパティには2種類あります。 + +1つ目は *データプロパティ* です。我々は既にそれがどのように動作するのか知っています。実際、これまで使ってきたすべてのプロパティはデータプロパティでした。 + +2つ目のプロパティの種類は新しいものです。それは *アクセサプロパティ* です。これらは基本的には値の取得やセットをする関数ですが、外部コードからは通常のプロパティのように見えます。 + +## Getters と setters + +アクセサプロパティは "getter" と "setter" メソッドで表現されます。オブジェクトリテラルでは、`get` と `set` で表されます: + +```js +let obj = { + *!*get propName()*/!* { + // getter, obj.propName を取得するときにコードが実行されます + }, + + *!*set propName(value)*/!* { + // setter, obj.propName = value 時にコードが実行されます + } +}; +``` + +`obj.propName` が読まれたときに getter は動作し、setter は割り当てられたときに動作します。 + +例えば、`name` と `surname` を持つ `user` オブジェクトがあります。: + +```js run +let user = { + name: "John", + surname: "Smith" +}; +``` + +今、"John Smith" という値を持つ "fullName" プロパティを追加したいとします。もちろん、既存の情報のコピーペーストはしたくありません。ここで、アクセサを使用して実装することができます。: + +```js run +let user = { + name: "John", + surname: "Smith", + +*!* + get fullName() { + return `${this.name} ${this.surname}`; + } +*/!* +}; + +*!* +alert(user.fullName); // John Smith +*/!* +``` + +外部からは、アクセサプロパティは通常の変数に見えます。それがアクセサプロパティの考え方です。関数として `user.fullName` を *呼び出すのではなく*、通常通り *読み込みます*。: getter は背後で実行されます。 + +今のところ、`fullName` は getter しか持っていません。`user.fullName =` を指定しようとすると、エラーになります。 + +```js run +let user = { + get fullName() { + return `...`; + } +}; + +*!* +user.fullName = "Test"; // Error (property has only a getter) +*/!* +``` + +`user.fullName` の setter を追加して修正しましょう。: + +```js run +let user = { + name: "John", + surname: "Smith", + + get fullName() { + return `${this.name} ${this.surname}`; + }, + +*!* + set fullName(value) { + [this.name, this.surname] = value.split(" "); + } +*/!* +}; + +// set fullName は指定された値で実行されます +user.fullName = "Alice Cooper"; + +alert(user.name); // Alice +alert(user.surname); // Cooper +``` + +結果、"仮想" プロパティ `fullName` を持っており、これは読み書き可能です。 + +## アクセサディスクリプタ + +アクセサプロパティのディスクリプタは、データプロパティと比べて異なります。 + +アクセサプロパティには、`value` も `writable` もありませんが、代わりに、`get` と `set` があります。 + +したがって、アクセサディスクリプタには次のものがあります: + +- **`get`** -- 引数なしの関数で、プロパティが読まれたときに動作します。 +- **`set`** -- 1つの引数をもつ関数で、プロパティがセットされたときに呼ばれます。 +- **`enumerable`** -- データプロパティと同じです。 +- **`configurable`** -- データプロパティと同じです。 + +例えば、アクセサ `fullName` を `defineProperty` で作るとき、`get` と `set` をディスクリプタに渡すことができます。: + +```js run +let user = { + name: "John", + surname: "Smith" +}; + +*!* +Object.defineProperty(user, 'fullName', { + get() { + return `${this.name} ${this.surname}`; + }, + + set(value) { + [this.name, this.surname] = value.split(" "); + } +*/!* +}); + +alert(user.fullName); // John Smith + +for(let key in user) alert(key); // name, surname +``` + +プロパティはアクセサ(`get/set` メソッドを持つ)かデータプロパティ(`value`を持つ)のいずれかになれますが、両方にはなれないことに注意してください。 + +`get` と `value` を同じディスクリプタで指定すると、エラーになります。: + +```js run +*!* +// Error: Invalid property descriptor. +*/!* +Object.defineProperty({}, 'prop', { + get() { + return 1 + }, + + value: 2 +}); +``` + +## スマートな getters/setters + +Getter/setter は、"実際の" プロパティ値のラッパーとして使用することで、それらをより詳細に制御することができます。 + +例えば、`user` で短すぎる名前を禁止したい場合、`name` を特別なプロパティ `_name` に格納することができます。そして、setter で値をフィルタします。: + +```js run +let user = { + get name() { + return this._name; + }, + + set name(value) { + if (value.length < 4) { + alert("Name is too short, need at least 4 characters"); + return; + } + this._name = value; + } +}; + +user.name = "Pete"; +alert(user.name); // Pete + +user.name = ""; // Name is too short... +``` + +そのため、 名前は `_name` プロパティに格納され、アクセスは getter/setter と通して行われます。 + +技術的には、外部コードは `user._name` を使うことで、直接 name にアクセスできるかもしれません。しかし、アンダースコア `"_"` で始まるプロパティは内部のもので、外部のオブジェクトから触るべきではないということは広く知られています。 + + +## 互換性のために使用する + +getter と setter の裏にある素晴らしいアイデアの1つは、それらは "通常の" データプロパティを制御し、それをいつでも調整することができることです。 + +例えば、データプロパティ `name` と `age` を使って user オブジェクトを実装し始めました。: + +```js +function User(name, age) { + this.name = name; + this.age = age; +} + +let john = new User("John", 25); + +alert( john.age ); // 25 +``` + +...しかし、遅かれ早かれそれを変更するかもしれません。より正確にするために、`age` の代わりに `birthday` を格納することに決めるかもしれません。: + +```js +function User(name, birthday) { + this.name = name; + this.birthday = birthday; +} + +let john = new User("John", new Date(1992, 6, 1)); +``` + +さて、まだ `age` プロパティを使っている古いコードはどうすればよいでしょうか? + +そのような箇所をすべて見つけて直していくこともできますが、時間がかかったり別の人が書いているコードであれば直すのが難しいかもしれません。その上、`age` は `user` が持っていても良いものですよね? + +そのままにしておきましょう。 + +`age` の getter を追加することで問題が解消できます: + +```js run no-beautify +function User(name, birthday) { + this.name = name; + this.birthday = birthday; + +*!* + // age は現在の日付と誕生日から計算されます + Object.defineProperty(this, "age", { + get() { + let todayYear = new Date().getFullYear(); + return todayYear - this.birthday.getFullYear(); + } + }); +*/!* +} + +let john = new User("John", new Date(1992, 6, 1)); + +alert( john.birthday ); // birthday は利用可能です +alert( john.age ); // ...age も同様です +``` + +これで古いコードも機能しつつ、追加のプロパティも追加できました。 diff --git a/1-js/07-object-properties/index.md b/1-js/07-object-properties/index.md new file mode 100644 index 0000000000..4af7d08742 --- /dev/null +++ b/1-js/07-object-properties/index.md @@ -0,0 +1,4 @@ +# オブジェクトプロパティの設定 + +このセクションでは、オブジェクトに戻り、そのプロパティについて、より深く学んでいきます。 + diff --git a/1-js/08-error-handling/1-try-catch/article.md b/1-js/08-error-handling/1-try-catch/article.md deleted file mode 100644 index 3edf60400b..0000000000 --- a/1-js/08-error-handling/1-try-catch/article.md +++ /dev/null @@ -1,662 +0,0 @@ -# エラーハンドリング, "try..catch" - -どんなに我々のプログラミングが素晴らしくても、スクリプトがエラーになることはあります。それはミス、予期しないユーザ入力、間違ったサーバレスポンスやその他多くの理由により発生する可能性があります。 - -通常、エラーケースではスクリプトは "死" (即時に停止) に、それをコンソールに出力します。 - -しかし、エラーを "キャッチ" し、死ぬ代わりにより意味のあることをする構文構造 `try..catch` があります。 - -[cut] - -## "try..catch" 構文 - -`try..catch` 構造は2つのメインブロックを持っています: `try` と `catch` です。: - -```js -try { - - // code... - -} catch (err) { - - // error handling - -} -``` - -それは次のように動作します: - -1. まず、`try {...}` のコードが実行されます。 -2. エラーがなければ、`catch(err)` は無視されます: 実行が `try` の最後に到達した後、`catch` を飛び越えます。 -3. エラーが発生した場合、`try` の実行が停止し、コントロールフローは `catch(err)` の先頭になります。`err` 変数(任意の名前が使えます)は発生した事象に関する詳細をもつエラーオブジェクトを含んでいます。 - -![](try-catch-flow.png) - -従って、`try {…}` ブロックの内側のエラーはスクリプトを殺しません: `catch` の中でそれを扱う機会が持てます。 - -より多くの例を見てみましょう。 - -- エラーなしの例: `alert` `(1)` と `(2)` を表示します: - - ```js run - try { - - alert('Start of try runs'); // *!*(1) <--*/!* - - // ...ここではエラーはありません - - alert('End of try runs'); // *!*(2) <--*/!* - - } catch(err) { - - alert('Catch is ignored, because there are no errors'); // (3) - - } - - alert("...Then the execution continues"); - ``` -- エラーの例: `(1)` と `(3)` を表示します: - - ```js run - try { - - alert('Start of try runs'); // *!*(1) <--*/!* - - *!* - lalala; // エラー, 変数は宣言されていません! - */!* - - alert('End of try (never reached)'); // (2) - - } catch(err) { - - alert(`Error has occured!`); // *!*(3) <--*/!* - - } - - alert("...Then the execution continues"); - ``` - - -````warn header="`try..catch` は実行時エラーにのみ作用します" -`try..catch` を動作させるために、コードは実行可能でなければなりません。つまり、有効なJavaScriptである必要があります。 - -もしコードが構文的に誤っている場合には動作しません。例えば次は角括弧の不一致です: - -```js run -try { - {{{{{{{{{{{{ -} catch(e) { - alert("The engine can't understand this code, it's invalid"); -} -``` - -JavaScriptエンジンは最初にコードを読み、次にそれを実行します。読み込みのフェーズで発生したエラーは "解析時間(parse-time)" エラーと呼ばれ、回復不能です(コードの内部からは)。なぜなら、エンジンはそのコードを理解することができないからです。 - -そのため、`try..catch` は有効なコードの中で起きたエラーのみを扱うことができます。このようなエラーは "ランタイムエラー" または "例外" と呼ばれます。 -```` - - -````warn header="`try..catch` は同期的に動作します" -もし `setTimeout` の中のような "スケジュールされた" コードで例外が発生した場合、`try..catch` はそれをキャッチしません。: - -```js run -try { - setTimeout(function() { - noSuchVariable; // スクリプトはここで死にます - }, 1000); -} catch (e) { - alert( "won't work" ); -} -``` - -`try..catch` は実際には関数をスケジュールする `setTimeout` 呼び出しをラップするためです。しかし関数自身は後で実行され、その時エンジンはすでに `try..catch` 構造を抜けています。 - -スケジュールされた関数の内側の例外をキャッチするためには、その関数の中に `try..catch` が必要です。: -```js run -setTimeout(function() { - try { - noSuchVariable; // try..catch がエラーをハンドリングします! - } catch (e) { - alert( "error is caught here!" ); - } -}, 1000); -``` -```` - -## エラーオブジェクト - -エラーが発生したとき、JavaScript はその詳細を含めたオブジェクトを生成します。そして `catch` の引数として渡されます。: - -```js -try { - // ... -} catch(err) { // <-- "エラーオブジェクト", err の代わりに別の名前を使うこともできます - // ... -} -``` - -すべての組み込みのエラーに対して、`catch` ブロック内のエラーオブジェクトは2つの主なプロパティを持っています。: - -`name` -: エラー名です。未定義変数の場合、それは `"ReferenceError"` です。 - -`message` -: エラー詳細に関するテキストメッセージです。 - -ほとんどの環境では、その他非標準のプロパティが利用可能です。最も広く使われ、サポートされているのは以下です: - -`stack` -: 現在のコールスタックです: エラーに繋がったネスト呼び出しのシーケンスに関する情報を持つ文字列です。デバッグ目的で使われます。 - -例: - -```js run untrusted -try { -*!* - lalala; // エラー, 変数が宣言されていません! -*/!* -} catch(err) { - alert(err.name); // ReferenceError - alert(err.message); // lalala is not defined - alert(err.stack); // ReferenceError: lalala is not defined at ... - - // 全体としてエラーを表示する事もできます - // エラーは "name: message" として文字列に変換されます - alert(err); // ReferenceError: lalala is not defined -} -``` - - -## "try..catch" の利用 - -`try..catch` の実際のユースケースについて探索してみましょう。 - -既にご存知の通り、JavaScriptは JSONエンコードされた値を読むためのメソッド [JSON.parse(str)](mdn:js/JSON/parse) がサポートされています。 - -通常、それはネットワーク経由でサーバまたは別のソースから受信したデータをデコードするために使われます。 - -今、次のようにデータを受信し、`JSON.parse` を呼び出します。: - -```js run -let json = '{"name":"John", "age": 30}'; // サーバからのデータ - -*!* -let user = JSON.parse(json); // テキスト表現をJSオブジェクトに変換 -*/!* - -// 今、 user 文字列からプロパティを持つオブジェクトです -alert( user.name ); // John -alert( user.age ); // 30 -``` - -JSON に関する詳細な情報は、チャプター を参照してください。 - -**`json` が不正な形式の場合、`JSON.parse` はエラーになるのでスクリプトは "死にます"。** - -それで満足しますか?もちろん満足しません! - -この方法だと、もしデータが何か間違っている場合、訪問者はそれを知ることができません(開発者コンソールを開かない限り)。また、人々は、エラーメッセージなしで何かが "単に死んでいる" ことを本当に本当に嫌います。 - -エラーを扱うために `try..catch` を使いましょう。: - -```js run -let json = "{ bad json }"; - -try { - -*!* - let user = JSON.parse(json); // <-- エラーが起きたとき... -*/!* - alert( user.name ); // 動作しません - -} catch (e) { -*!* - // ...実行はここに飛びます - alert( "Our apologies, the data has errors, we'll try to request it one more time." ); - alert( e.name ); - alert( e.message ); -*/!* -} -``` - -ここでは、メッセージを表示するためのだけに `catch` ブロックを使っていますが、より多くのことをすることができます。: 新たなネットワーク要求、訪問者への代替手段の提案、ロギング機構へエラーに関する情報の送信... すべて、単に死ぬよりははるかに良いです。 - -## 我々独自のエラーをスローする - -仮に `json` が構文的に正しいが、必須の `"name"` プロパティを持っていない場合どうなるでしょう? - -このように: - -```js run -let json = '{ "age": 30 }'; // 不完全なデータ - -try { - - let user = JSON.parse(json); // <-- エラーなし -*!* - alert( user.name ); // name はありません! -*/!* - -} catch (e) { - alert( "doesn't execute" ); -} -``` - -ここで、`JSON.parse` は通常どおり実行しますが、`"name"` の欠落は実際には我々にとってはエラーです。 - -エラー処理を統一するために、`throw` 演算子を使います。 - -### "Throw" 演算子 - -`throw` 演算子はエラーを生成します。 - -構文は次の通りです: - -```js -throw -``` - -技術的には、エラーオブジェクトとしてなんでも使うことができます。たとえ、数値や文字列のようなプリミティブでもOKです。しかし、`name` と `message` プロパティを持つオブジェクトを使うのがベターです(組み込みのエラーと互換性をいくらか保つために)。 - -JavaScriptは標準エラーのための多くの組み込みのコンストラクタを持っています: `Error`, `SyntaxError`, `ReferenceError`, `TypeError` などです。私たちは同じようにエラーオブジェクトを作るのにそれらが使えます。 - -構文は次の通りです: - -```js -let error = new Error(message); -// or -let error = new SyntaxError(message); -let error = new ReferenceError(message); -// ... -``` - -組み込みのエラー(任意のオブジェクトではなく、エラーのみ)では、`name` プロパティはコンストラクタの名前と全く同じになります。そして `message` は引数から取られます。 - -例: - -```js run -let error = new Error("Things happen o_O"); - -alert(error.name); // Error -alert(error.message); // Things happen o_O -``` - -`JSON.parse` が生成するエラーの種類を見てみましょう: - -```js run -try { - JSON.parse("{ bad json o_O }"); -} catch(e) { -*!* - alert(e.name); // SyntaxError -*/!* - alert(e.message); // Unexpected token o in JSON at position 0 -} -``` - -ご覧の通り、それは `SyntaxError` です。 - -...そして、我々のケースでは、ユーザは必ず `"name"` を持っていると仮定するので、`name` の欠落もまた構文エラーとして扱います。 - -なので、それをスローするようにしましょう: - -```js run -let json = '{ "age": 30 }'; // 不完全なデータ - -try { - - let user = JSON.parse(json); // <-- エラーなし - - if (!user.name) { -*!* - throw new SyntaxError("Incomplete data: no name"); // (*) -*/!* - } - - alert( user.name ); - -} catch(e) { - alert( "JSON Error: " + e.message ); // JSON Error: Incomplete data: no name -} -``` - -行 `(*)` で、`throw` 演算子が与えられた `message` で `SyntaxError` を生成します。それは JavaScript が生成するのと同じ方法です。`try` の実行はすぐに停止し、制御フローは `catch` に移ります。 - -今や、`catch` はすべてのエラーハンドリングのための1つの場所になりました。: `JSON.parse` と他のケース。 - -## 再スロー - -上の例で、私たちは不正なデータを処理するために `try..catch` を使っています。しかし、`try {...}` ブロックの中で *別の予期しないエラー* が発生する可能性はあるでしょうか? 変数が未定義、またはその他、単に "不正なデータ" ではない何か。 - -このように: - -```js run -let json = '{ "age": 30 }'; // 不完全なデータ - -try { - user = JSON.parse(json); // <-- user の前に "let" をつけ忘れた - - // ... -} catch(err) { - alert("JSON Error: " + err); // JSON Error: ReferenceError: user is not defined - // (実際にはJSONのエラーではありません) -} -``` - -もちろん、すべての可能性があります! プログラマはミスをするものです。何十年も何百万人もの人が使っているオープンソースのユーティリティであっても、突然酷いバグが発見され、ひどいハッキングにつながることがあります( `ssh` ツールで起こったようなものです)。 - -私たちのケースでは、`try..catch` は "不正なデータ" エラーをキャッチすることを意図しています。しかし、その性質上、`catch` は `try` からの *すべての* エラーを取得します。ここでは予期しないエラーが発生しますが、同じ `"JSON Error"` メッセージが表示されます。それは誤りでコードのでバッグをより難しくします。 - -幸いにも、どのエラーを取得したかを知ることができます。例えば、`name` から: - -```js run -try { - user = { /*...*/ }; -} catch(e) { -*!* - alert(e.name); // 未定義変数へのアクセスに対する "ReferenceError" -*/!* -} -``` - -ルールはシンプルです。: - -**キャッチはそれが知っているエラーだけを処理し、すべてのオブジェクトを "再スロー" するべきです** - -"再スロー" テクニックの詳細は次のように説明できます: - -1. すべてのエラーをキャッチします。 -2. `catch(err) {...}` ブロックで、エラーオブジェクト `err` を解析します。 -3. どう処理すればいいか分からなければ、`throw err` をします。 - -下のコードでは、`catch` が `SyntaxError` だけを処理するよう再スローを使っています。: - -```js run -let json = '{ "age": 30 }'; // 不完全なデータ -try { - - let user = JSON.parse(json); - - if (!user.name) { - throw new SyntaxError("Incomplete data: no name"); - } - -*!* - blabla(); // 予期しないエラー -*/!* - - alert( user.name ); - -} catch(e) { - -*!* - if (e.name == "SyntaxError") { - alert( "JSON Error: " + e.message ); - } else { - throw e; // 再スロー (*) - } -*/!* - -} -``` - -行 `(*)` での、`catch` ブロック内部からのエラーのスローは `try..catch` を "抜けて" 外部の `try..catch` 構造(存在する場合)でキャッチされる、またはスクリプトをキルします。 - -従って、`catch` ブロックは実際に扱い方を知っているエラーだけを処理しその他すべてを "スキップ" します。 - -下の例は、このようなエラーが1つ上のレベルの `try..catch` で捕捉されるデモです: - -```js run -function readData() { - let json = '{ "age": 30 }'; - - try { - // ... -*!* - blabla(); // error! -*/!* - } catch (e) { - // ... - if (e.name != 'SyntaxError') { -*!* - throw e; // 再スロー (今のエラーの扱い方を知らない) -*/!* - } - } -} - -try { - readData(); -} catch (e) { -*!* - alert( "External catch got: " + e ); // caught it! -*/!* -} -``` - -ここでは、`readData` は `SyntaxError` の処理の仕方だけ知っており、外部の `try..catch` はすべての処理の方法を知っています。 - -## try..catch..finally - -待ってください、それですべてではありません。 - -`try..catch` 構造はもう1つのコード句: `finally` を持つ場合があります。 - -もし存在する場合、それはすべてのケースで実行します。: - -- エラーが無かった場合は、`try` の後で。 -- エラーがあった場合には `catch` の後で。 - -拡張された構文はこのように見えます。: - -```js -*!*try*/!* { - ... コードを実行しようとします ... -} *!*catch*/!*(e) { - ... エラーを処理します ... -} *!*finally*/!* { - ... 常に実行します ... -} -``` - -このコードを実行してみましょう。: - -```js run -try { - alert( 'try' ); - if (confirm('Make an error?')) BAD_CODE(); -} catch (e) { - alert( 'catch' ); -} finally { - alert( 'finally' ); -} -``` - -このコードは2つの実行方法があります。: - -1. もし "Make an error" に "Yes" と答えると、`try -> catch -> finally` となります。 -2. もし "No" と言えば、`try -> finally` となります。 - -`finally` 句は `try..catch` の前に何かを開始して、どのような結果であれファイナライズをしたいときに頻繁に使われます。 - -例えば、フィボナッチ数関数 `fib(n)` にかかる時間を計測したいとします。当然ながら、それを実行する前に計測を開始して、実行後に終了させることができます。しかし、仮に関数呼び出しの間でエラーが起きたらどうなるでしょう?特に下のコードの `fib(n)` の実装では、負の値または非整数値だとエラーを返します。 - -`finally` 句は何があっても計測を完了させるのに良い場所です。 - -ここで、`finally` は両方のシチュエーション -- `fib` の実行が成功するケースと失敗するケース -- で時間が正しく計測されることを保証します。: - -```js run -let num = +prompt("Enter a positive integer number?", 35) - -let diff, result; - -function fib(n) { - if (n < 0 || Math.trunc(n) != n) { - throw new Error("Must not be negative, and also an integer."); - } - return n <= 1 ? n : fib(n - 1) + fib(n - 2); -} - -let start = Date.now(); - -try { - result = fib(num); -} catch (e) { - result = 0; -*!* -} finally { - diff = Date.now() - start; -} -*/!* - -alert(result || "error occured"); - -alert( `execution took ${diff}ms` ); -``` - -コードを実行して `prompt` に `35` を入力することで確認できます -- 通常 `try` の後に `finally` を実行します。そして `-1` を入れると -- すぐにエラーになり、その実行は `0ms` となります。両方の計測は正しく行われています。 - -つまり、関数を終了するには方法が2つあります: `return` または `throw` です。 `finally` 句はそれら両方とも処理します。 - -```smart header="変数は `try..catch..finally` の内部でローカルです" -上のコードで `result` と `diff` 変数は `try..catch` の *前* で宣言されていることに注意してください。 - -そうでなく、`let` が `{...}` ブロックの中で作られている場合、その中でしか見えません。 -``` - -````smart header="`finally` と `return`" -Finally 句は `try..catch` からの *任意の* 終了に対して機能します。それは明白な `return` も含みます。 - -下の例では、`try` の中で `return` があります。この場合、`finally` は制御が外部コードに戻る前に実行されます。 - -```js run -function func() { - - try { -*!* - return 1; -*/!* - - } catch (e) { - /* ... */ - } finally { -*!* - alert( 'finally' ); -*/!* - } -} - -alert( func() ); // 最初に finally の alert が動作し、次にこれが動作します -``` -```` - -````smart header="`try..finally`" - -`catch` 句がない `try..catch` 構造も役立ちます。私たちはここでエラーを正しく処理したくないが、開始した処理が完了したことを確認したいときに使います。 - -```js -function func() { - // (計測など)完了させる必要のあるなにかを開始する - try { - // ... - } finally { - // すべてが死んでいても完了させる - } -} -``` -上のコードでは、`try` の内側のエラーは常に抜けます。なぜなら `catch` がないからです。しかし `finally` は実行フローが外部に移る前に機能します。 -```` - -## グローバルな catch - -```warn header="環境特有" -このセクションの情報はコアなJavaScriptの一部ではありません。 -``` - -`try..catch` の外側で致命的なエラーが起きてスクリプトが死んだことをイメージしてください。プログラミングエラーやその他何か酷いものによって。 - -そのような出来事に反応する方法はありますか? エラーをログに記録したり、ユーザーに何かを見せたり(通常はエラーメッセージが表示されません)。 - -仕様ではそのようなものはありませんが、通常、環境がそれを提供しています。なぜなら本当に有用だからです。例えば、Node.JS はそのために [process.on('uncaughtException')](https://nodejs.org/api/process.html#process_event_uncaughtexception)を持っています。 -また、ブラウザでは関数を特別な [window.onerror](mdn:api/GlobalEventHandlers/onerror) プロパティに代入することができます。それはキャッチしていないエラーの場合に実行されます。 - -構文: - -```js -window.onerror = function(message, url, line, col, error) { - // ... -}; -``` - -`message` -: エラーメッセージ - -`url` -: エラーが起きたスクリプトのURL - -`line`, `col` -: エラーが起きた行と列番号 - -`error` -: エラーオブジェクト - -例: - -```html run untrusted refresh height=1 - -``` - -グローバルハンドラー `window.onerror` の役割は、通常スクリプトの実行の回復ではありません -- プログラミングエラーの場合、恐らくそれは不可能なので開発者にエラーメッセージを送ります。 - -このようなケースでエラーログを提供する web サービスもあります。https://errorception.com> や 。 - -それらは次のように動きます: - -1. 私たちはサービスに登録し、ページにそうにゅうするためのJSのピース(またはスクリプトのURL)をそれらから得ます。 -2. そのJSスクリプトはカスタムの `window.onerror` 関数を持っています。 -3. エラーが起きた時、そのサービスへネットワークリクエストを送ります。 -4. 私たちはサービスのWebインタフェースにログインしてエラーを見ることができます。 - -## サマリ - -`try..catch` 構造はランタイムエラーを処理することができます。文字通りコードを実行しようと試みて、その中で起こるエラーをキャッチします。 - -構文は次の通りです: - -```js -try { - // コードを実行 -} catch(err) { - // エラーが起きた場合、ここにジャンプ - // err はエラーオブジェクト -} finally { - // すべてのケースで try/catch 後に実行する -} -``` - -`catch` セクションがない、または `finally` がない場合があります。なので `try..catch` と `try..finally` もまた有効です。 - -エラーオブジェクトは次のプロパティを持っています。: - -- `message` -- 人が読めるエラーメッセージです。 -- `name` -- エラー名を指す文字列です(エラーコンストラクタ名) -- `stack` (非標準) -- エラー生成時のスタックです。 - -また、`throw` 演算子を使って独自のエラーを生成することもできます。技術的には、`throw` の引数は何でもよいですが、通常は組み込みの `Error` クラスを継承しているエラーオブジェクトです。次のチャプターでエラーを拡張する方法について詳しく説明します。 - -再スローはエラーハンドリングの基本パターンです。: `catch` ブロックは通常、特定のエラータイプを処理する方法を予期し、知っています。したがって、知らないエラーは再スローすべきです。 - -たとえ `try..catch` を持っていない場合でも、ほとんどの環境では "抜け出た" エラーをキャッチするために "グローバル" なエラーハンドラを設定することができます。ブラウザでは、それは `window.onerror` です。 diff --git a/1-js/08-error-handling/1-try-catch/try-catch-flow.png b/1-js/08-error-handling/1-try-catch/try-catch-flow.png deleted file mode 100644 index 6a91b63295..0000000000 Binary files a/1-js/08-error-handling/1-try-catch/try-catch-flow.png and /dev/null differ diff --git a/1-js/08-error-handling/1-try-catch/try-catch-flow@2x.png b/1-js/08-error-handling/1-try-catch/try-catch-flow@2x.png deleted file mode 100644 index 8bf9680fd6..0000000000 Binary files a/1-js/08-error-handling/1-try-catch/try-catch-flow@2x.png and /dev/null differ diff --git a/1-js/08-error-handling/2-custom-errors/article.md b/1-js/08-error-handling/2-custom-errors/article.md deleted file mode 100644 index 27a8813f11..0000000000 --- a/1-js/08-error-handling/2-custom-errors/article.md +++ /dev/null @@ -1,310 +0,0 @@ -# カスタムエラー, Error の拡張 - -何かを開発するとき、我々のタスクで間違っているかもしれない特定の内容をエラー内容に反映するために、独自のエラークラスが必要になることがよくあります。 -ネットワーク操作のエラーについては、 `HttpError`、データベース操作 `DbError`、検索操作 `NotFoundError` などが必要な場合があります。 - -エラーは `message`, `name` 、望ましくは `stack` のような基本のエラープロパティをサポートするべきです。しかし、他にも独自のプロパティを持つかもしれません。例えば `HttpError` オブジェクトであれば、 `404`, `403` もしくは `500` といった値をとる `statusCode` プロパティを持つかもしれません。 - -JavaScript は任意の引数で `throw` できるので、技術的にはカスタムのエラークラスは `Error` から継承する必要はありません。しかし、継承しているとエラーオブジェクトを識別する `obj instanceof Error` を使えるようになります。そのため、継承しておくほうのがベターです。 - -アプリケーションを開発するにつれ、独自のエラーが自然に階層を形成します。たとえば、 `HttpTimeoutError` は `HttpError` を継承する、といったように。 - -## Error を拡張する - -例として、ユーザデータをもつ JSON を読む関数 `readUser(json)` を考えてみましょう。 - -ここでは、有効な `json` がどのように見えるかの例を示します。: -```js -let json = `{ "name": "John", "age": 30 }`; -``` - -内部的には、`JSON.parse` を使います。もし不正な `json` を受け取った場合、それは `SyntaxError` をスローします。 - -しかし、たとえ `json` が構文的に正しくても、それが正しいユーザとは限りません。 それには必要なデータが不足しているかもしれません。例えば、我々のケースだと、ユーザに必要不可欠な `name` や `age` プロパティを持っていない場合です。 - -私たちの関数 `readUser(json)` はJSONを読むだけでなく、データのチェック(バリデート)をします。もし必須のフィールドがなかったり、フォーマットが間違っている場合、エラーです。そしてそれは `SyntaxError` ではありません。なぜならデータは構文的には正しく、別の種類のエラーだからです。したがって、それを `ValidationError` と呼び、そのためのクラスを作りましょう。このようなエラーは、問題のあるフィールドに関する情報も保持する必要があります。 - -我々の `ValidationError` クラスは組み込みの `Error` クラスから継承します。 - -そのクラスは組み込みですが、そのクラスのおおよそのコードを書いて、私たちが何を拡張しているのかを理解する必要があります。 - -これです: - -```js -// JavaScript自体で定義された組み込みのErrorクラスの「擬似コード」 -class Error { - constructor(message) { - this.message = message; - this.name = "Error"; // (組み込みのエラークラスごとに異なる名前) - this.stack = ; // non-standard, but most environments support it - } -} -``` - -では、話を戻して `ValidationError` をそれから継承させましょう。: - -```js run untrusted -*!* -class ValidationError extends Error { -*/!* - constructor(message) { - super(message); // (1) - this.name = "ValidationError"; // (2) - } -} - -function test() { - throw new ValidationError("Whoops!"); -} - -try { - test(); -} catch(err) { - alert(err.message); // Whoops! - alert(err.name); // ValidationError - alert(err.stack); // それぞれの行番号を持つネストされたコールのリスト -} -``` - -コンストラクタを見てください: - -1. 行 `(1)` で、親のコンストラクタを読んでいます。JavaScriptは子のコンストラクタ内での `super` の呼び出しは必須なので、それは義務です。親のコンストラクタは `message` プロパティをセットします。 -2. 親のコンストラクタは `name` プロパティも `"Error"` へセットしますので、行 `(2)` で正しい値にリセットしています。 - -`readUser(json)` で使ってみましょう: - -```js run -class ValidationError extends Error { - constructor(message) { - super(message); - this.name = "ValidationError"; - } -} - -// Usage -function readUser(json) { - let user = JSON.parse(json); - - if (!user.age) { - throw new ValidationError("No field: age"); - } - if (!user.name) { - throw new ValidationError("No field: name"); - } - - return user; -} - -// try..catch での動作例 - -try { - let user = readUser('{ "age": 25 }'); -} catch (err) { - if (err instanceof ValidationError) { -*!* - alert("Invalid data: " + err.message); // Invalid data: No field: name -*/!* - } else if (err instanceof SyntaxError) { // (*) - alert("JSON Syntax Error: " + err.message); - } else { - throw err; // 知らないエラーなので、再スロー - } -} -``` - -上のコードの `try.catch` ブロックは `ValidationError` と `JSON.parse` からの組み込みの `SyntaxError` 両方を処理します。 - -行 `(*)` で特定のエラータイプのチェックをするために、どのように `instanceof` を使っているか見てください。 - -このようにして `err.name` を見ることもできます。: - -```js -// ... -// (err instanceof SyntaxError) の代わり -} else if (err.name == "SyntaxError") { // (*) -// ... -``` - -`instanceof` の方がよりベターです。なぜなら、将来 `ValidationError` を拡張し、`PropertyRequiredError` のようなサブタイプを作るからです。そして `instanceof` チェックは新しい継承したクラスでもうまく機能し続けます。それは将来を保証します。 - -また、`catch` が未知のエラーに遭遇したとき、行 `(**)` でそれを再度スローすることも重要です。 `catch` はバリデーションと構文エラーの処理の仕方だけを知っています。他の種類(コード中のタイポやその他)の場合は失敗します。 - -## さらなる継承 - -`ValidationError` クラスはとても汎用的です。色んな種類の間違いがあるかもしれません -- プロパティが存在しなかったり、誤ったフォーマット(`age` が文字列値のような)であるかもしれません。厳密に存在しないプロパティに対して、より具体的なクラス `PropertyRequiredError` を作りましょう。欠落しているプロパティについての追加の情報を保持します。 - - -```js run -class ValidationError extends Error { - constructor(message) { - super(message); - this.name = "ValidationError"; - } -} - -*!* -class PropertyRequiredError extends ValidationError { - constructor(property) { - super("No property: " + property); - this.name = "PropertyRequiredError"; - this.property = property; - } -} -*/!* - -// 使用法 -function readUser(json) { - let user = JSON.parse(json); - - if (!user.age) { - throw new PropertyRequiredError("age"); - } - if (!user.name) { - throw new PropertyRequiredError("name"); - } - - return user; -} - -// try..catch での動作例 - -try { - let user = readUser('{ "age": 25 }'); -} catch (err) { - if (err instanceof ValidationError) { -*!* - alert("Invalid data: " + err.message); // Invalid data: No property: name - alert(err.name); // PropertyRequiredError - alert(err.property); // name -*/!* - } else if (err instanceof SyntaxError) { - alert("JSON Syntax Error: " + err.message); - } else { - throw err; // 知らないエラーなので、それを再スロー - } -} -``` - -新しいクラス `PropertyRequiredError` は簡単に使うことができます: プロパティ名を渡すだけです。: `new PropertyRequiredError(property)`。人が読める `message` はコンストラクタで作られます。 - -`PropertyRequiredError` コンストラクタでの `this.name` は再度手動で割り当てられることに注意してください。それは少しうんざりするかもしれません -- カスタムのエラーを作る度に `this.name = ` と代入することに。しかし方法があります。コンストラクタの中で `this.name` に対して `this.constructor.name` を使うことで、我々の肩の荷をとってくれる独自の "基本エラー" クラスを作ることができます。そしてそれを継承します。 - -それは `MyError` と呼びましょう。 - -ここでは、単純化した `MyError` のコードと他のカスタムエラークラスを示します。: - -```js run -class MyError extends Error { - constructor(message) { - super(message); -*!* - this.name = this.constructor.name; -*/!* - } -} - -class ValidationError extends MyError { } - -class PropertyRequiredError extends ValidationError { - constructor(property) { - super("No property: " + property); - this.property = property; - } -} - -// name is correct -alert( new PropertyRequiredError("field").name ); // PropertyRequiredError -``` - -これで、カスタムエラーははるかに短くなりました。特に `ValidationError` はコンストラクタの `"this.name = ..."` の行を除いたので。 - -## 例外のラッピング - -上のコードの関数 `readUser` の目的は "ユーザデータを読むこと" ですよね? この処理では異なる種類のエラーが起こる可能性があります。今は `SyntaxError` と `ValidationError` を持っていますが、将来 `readUser` 関数が成長するかもしれません: 新たなコードが別の種類のエラーを生み出すかもしれません。 - -`readUser` を呼び出すコードは、これらのエラーを処理する必要があります。今は `catch` ブロックの中で、異なるエラータイプのチェックと未知のエラーを再スローするために、複数の `if` を使っています。しかし、もし `readUser` 関数が複数の種類のエラーを生成する場合 -- `readUser` 呼び出しをするすべてのコードで、本当にすべてのエラータイプを1つずつチェックしたいですか? - -答えは、"いいえ" です。: 外側のコードは "それらすべての1つ上のレベル" でありたいです。つまり "データ読み込みエラー" でいくつかの種類を持ちたいです。正確になぜそれが起きたのか -- はしばしば重要ではありません(エラーメッセージがそれを説明します)。もしくは、必要な場合にのみ、エラーの詳細を取得方法があると更にベターです。 - -従って、このようなエラーを表現するための新しいクラス `ReadError` を作りましょう。`readUser` の中でエラーが起きると、それをキャッチし、`ReadError` を生成します。その `cause` プロパティで、元のエラーへの参照を持っておきます。そして外部のコードは `ReadError` に対してのみチェックを行います。 - -これは、`ReadError` を定義し、`readUser` と `try..catch` でそれを利用するデモです。: - -```js run -class ReadError extends Error { - constructor(message, cause) { - super(message); - this.cause = cause; - this.name = 'ReadError'; - } -} - -class ValidationError extends Error { /*...*/ } -class PropertyRequiredError extends ValidationError { /* ... */ } - -function validateUser(user) { - if (!user.age) { - throw new PropertyRequiredError("age"); - } - - if (!user.name) { - throw new PropertyRequiredError("name"); - } -} - -function readUser(json) { - let user; - - try { - user = JSON.parse(json); - } catch (err) { -*!* - if (err instanceof SyntaxError) { - throw new ReadError("Syntax Error", err); - } else { - throw err; - } -*/!* - } - - try { - validateUser(user); - } catch (err) { -*!* - if (err instanceof ValidationError) { - throw new ReadError("Validation Error", err); - } else { - throw err; - } -*/!* - } - -} - -try { - readUser('{bad json}'); -} catch (e) { - if (e instanceof ReadError) { -*!* - alert(e); - // Original error: SyntaxError: Unexpected token b in JSON at position 1 - alert("Original error: " + e.cause); -*/!* - } else { - throw e; - } -} -``` - -上のコードで、`readUser` は説明されている通りに正確に動作します -- 構文とバリデーションエラーをキャッチし、`ReadError` エラーを代わりにスローします(未知のエラーは通常通り再スローします)。 - -なので、外部のコードは `instanceof ReadError` をチェックするだけです。可能性のあるすべてのエラータイプをリストする必要はありません。 - -このアプローチは、"低レベルの例外" を取り除き、呼び出しコードで使用するより抽象的で便利な "ReadError" に "ラップ" するため、"例外のラッピング" と呼ばれます。 オブジェクト指向プログラミングで広く使用されています。 - -## サマリ - -- 私たちは、`Error` や他の組み込みのエラークラスから継承することができます。そのときは、`name` プロパティに手を入れることと、`super` -の呼び出しを忘れないでください。 -- ほとんどの場合、特定のエラーをチエックするために `instanceof` を使うべきです。それは継承している場合にも有効です。しかし、ときにはサードパーティのライブラリから来たエラーオブジェクトを持っていて、簡単にそのクラスを取得する方法がないことがあります。このようなチェックの場合には `name` プロパティを使うことができます。 -- 例外のラッピングは、関数が低レベルの例外を処理し、そのエラーを報告するために高レベルのオブジェクトを作る時のテクニックとして広く知られています。低レベルの例外は、上の例の `err.cause` のようにオブジェクトのプロパティになることがありますが、厳密には必須ではありません。 diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/1-property-after-delete/solution.md b/1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/1-property-after-delete/solution.md rename to 1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/solution.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/1-property-after-delete/task.md b/1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/1-property-after-delete/task.md rename to 1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/task.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/2-search-algorithm/solution.md b/1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/2-search-algorithm/solution.md rename to 1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/solution.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/2-search-algorithm/task.md b/1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/2-search-algorithm/task.md rename to 1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/task.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/3-proto-and-this/solution.md b/1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/3-proto-and-this/solution.md rename to 1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/solution.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/3-proto-and-this/task.md b/1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/3-proto-and-this/task.md rename to 1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/task.md diff --git a/1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/solution.md b/1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/03-prototype-inheritance/4-hamster-proto/solution.md rename to 1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/solution.md diff --git a/1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/task.md b/1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/task.md new file mode 100644 index 0000000000..2346b2df68 --- /dev/null +++ b/1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/task.md @@ -0,0 +1,35 @@ +importance: 5 + +--- + +# なぜ2匹のハムスターがお腹一杯? + +私たちは2匹のハムスターを持っています: `speedy` と `lazy` は一般的な `hamster` オブジェクトを継承しています。 + +そのうちの1匹に餌をやるとき、もう1匹もお腹一杯になります。なぜでしょう?どのように修正しますか? + +```js run +let hamster = { + stomach: [], + + eat(food) { + this.stomach.push(food); + } +}; + +let speedy = { + __proto__: hamster +}; + +let lazy = { + __proto__: hamster +}; + +// 一方が食べ物を見つけました +speedy.eat("apple"); +alert( speedy.stomach ); // apple + +// もう一方も持っています。なぜでしょう?修正してください。 +alert( lazy.stomach ); // apple +``` + diff --git a/1-js/08-prototypes/01-prototype-inheritance/article.md b/1-js/08-prototypes/01-prototype-inheritance/article.md new file mode 100644 index 0000000000..94c960f4ad --- /dev/null +++ b/1-js/08-prototypes/01-prototype-inheritance/article.md @@ -0,0 +1,339 @@ +# プロトタイプ継承 + +プログラミングでは、何かを取得しそれを拡張することがしばしばあります。 + +例えば、プロパティとメソッドをもつ `user` オブジェクトがあるとします。そして、そのいくつかを僅かに変更した `admin` や `guest` を作りたいとします。メソッドのコピーや再実装ではなく、単にその上に新しいオブジェクトを作成することで、`user` が持っているものを再利用したいです。 + +*プロトタイプ継承* はそれを助ける言語の機能です。 + +## プロトタイプ [[Prototype]] + +JavaScriptでは、オブジェクトは特別な隠しプロパティ `[[Prototype]]` (スペックにて命名されています)を持っており、それは `null` または別のオブジェクトを参照します。そのオブジェクトは "プロトタイプ" と呼ばれます。 + +![prototype](object-prototype-empty.svg) + +`object` からプロパティを読んだときに存在しない場合、JavaScriptは自動的にプロトタイプからそれを取得します。プログラミングではこのようなことを "プロトタイプ継承" と呼びます。多くのクールな言語機能やプログラミングテクニックは、これがベースになっています。 + +プロパティ `[[Prototype]]` は内部であり隠されていますが、セットする多くの方法があります。 + +それらの1つは、次のように `__proto__` を使う方法です: + +```js run +let animal = { + eats: true +}; +let rabbit = { + jumps: true +}; + +*!* +rabbit.__proto__ = animal; // rabbit.[[Prototype]] = animal をセット +*/!* +``` + +`rabbit` のプロパティを読むときにそれがない場合、JavaScriptは自動で `animal` から取得します。 + +例: + +```js run +let animal = { + eats: true +}; +let rabbit = { + jumps: true +}; + +*!* +rabbit.__proto__ = animal; // (*) +*/!* + +// 今、rabbit で両方のプロパティを見つけることができます: +*!* +alert( rabbit.eats ); // true (**) +*/!* +alert( rabbit.jumps ); // true +``` + +ここで、行 `(*)` は `rabbit` のプロトタイプに `animal` をセットしています。 + +次に、`alert` がプロパティ `rabbit.eats` `(**)` を読もうとしたとき、それは `rabbit` にはないので、JavaScriptは `[[Prototype]]` 参照に従って、`animal` の中でそれを見つけます(下から上に向かいます)。 + +![](proto-animal-rabbit.svg) + +ここでは、私たちは "`animal` は `rabbit` のプロトタイプ" または "`rabbit` がプロトタイプ的に `animal` を継承している" という事ができます。" + +したがって、もし `animal` が多くの役立つプロパティやメソッドを持っている場合、それらは自動的に `rabbit` でも利用可能になります。このようなプロパティは "継承" と呼ばれます。 + +もし `animal` がメソッドを持っている場合、`rabbit` でもそれを呼ぶことができます: + +```js run +let animal = { + eats: true, +*!* + walk() { + alert("Animal walk"); + } +*/!* +}; + +let rabbit = { + jumps: true, + __proto__: animal +}; + +// walk は prototype から得られました +*!* +rabbit.walk(); // Animal walk +*/!* +``` + +メソッドは次のように自動的にプロトタイプから取られます。: + +![](proto-animal-rabbit-walk.svg) + +プロトタイプチェーンは長くても問題ありません。: + +```js run +let animal = { + eats: true, + walk() { + alert("Animal walk"); + } +}; + +let rabbit = { + jumps: true, +*!* + __proto__: animal +*/!* +}; + +let longEar = { + earLength: 10, +*!* + __proto__: rabbit +*/!* +}; + +// walk は prototype チェーンから取られました +longEar.walk(); // Animal walk +alert(longEar.jumps); // true (rabbit から) +``` + +![](proto-animal-rabbit-chain.svg) + +ここで `longEar` から何かしらを読み取ろうとして、それが見つからなかった場合、JavaScript はまず `rabbit`、次に `animal` の順に探しに行きます。 + +実際には、2つの制限があります。: + +1. 参照を循環させることはできません。JavaScriptは、循環するように `__proto__` を割り当てようとするとエラーになります。 +2. `__proto__` の値はオブジェクトまたは `null` になります。プリミティブのような、それ以外のすべての値は無視されます。 + +また、明白かもしれませんが、1つの `[[Prototype]]` しか存在しません。 オブジェクトは2つの他のものから継承することはできません。 + + +```smart header="`__proto__` は `[[Prototype]]` のための歴史的な getter/setter です。" +この2つの違いを知らないのは、初心者の開発者にありがちなミスです。 + +`__proto__` は内部の `[[Prototype]]` プロパティとは *同じではない* ことに注意してください。これは、`[[Prototype]]` のgetter/setter です。後でそれが重要な場面をみていきますが、今は JavaScript 言語を理解するために、このプロパティについて心に留めておいてください。 + +`__proto__` プロパティは少し時代遅れです。これは歴史的な理由から存在しており、モダンな JavaScript では、プロトタイプの get/set の代わりに、 `Object.getPrototypeOf/Object.setPrototypeOf` 関数を使用することが推奨されています。これらの関数についても、後ほど説明していきます。 + +スペックでは、`__proto__` はブラウザでのみサポートされなければなりません。ですが、実際にはサーバサイドを含むすべての環境で`__proto__` がサポートされています。ので、安全に使用することができます。 + +`__proto__` 記法は直感的でわかりやすいので、例ではこの記法を使います。 +``` + +## 書き込みはプロトタイプを使用しません + +プロトタイプは、プロパティを読むためだけに使われます。 + +書き込み/削除操作はオブジェクトで直接動作します。 + +下の例では、自身の `walk` メソッドを `rabbit` に割り当てています。: + +```js run +let animal = { + eats: true, + walk() { + /* このメソッドは rabbit では使われません */ + } +}; + +let rabbit = { + __proto__: animal +} + +*!* +rabbit.walk = function() { + alert("Rabbit! Bounce-bounce!"); +}; +*/!* + +rabbit.walk(); // Rabbit! Bounce-bounce! +``` + +これ以降、`rabbit.walk()` 呼び出しは、プロトタイプを使うことなく、オブジェクトの中にすぐにメソッドを見つけ、それを実行します。 + +![](proto-animal-rabbit-walk-2.svg) + +getter/setter の場合 -- もしプロパティの読み書きをすると、プロトタイプで参照されて呼び出されます。 + +例えば、以下のコードで `admin.fullName` プロパティをチェックしてください: + +```js run +let user = { + name: "John", + surname: "Smith", + + set fullName(value) { + [this.name, this.surname] = value.split(" "); + }, + + get fullName() { + return `${this.name} ${this.surname}`; + } +}; + +let admin = { + __proto__: user, + isAdmin: true +}; + +alert(admin.fullName); // John Smith (*) + +// setter がトリガします! +admin.fullName = "Alice Cooper"; // (**) + +alert(admin.fullName); // Alice Cooper, admin の状態が変更されました。 +alert(user.fullName); // John Smith, user の状態はそのままです。 +``` + +ここで、行 `(*)` では、プロパティ `admin.fullName` はプロトタイプである `user` が getter を持っているので、それが呼ばれます。同様に行 `(**)` では、プロパティはプロトタイプにある setter が呼ばれます。 + +## "this" の値 + +上の例で、興味深い質問が起きるかもしれません。: `set fullName(value)` の内側での `this` の値はなんでしょうか?プロパティ `this.name` と `this.surname` が書かれているのはどこでしょうか? `user` または `admin` ? + +答えはシンプルです: `this` はプロトタイプによる影響を持ったく受けません。 + +**メソッドがどこにあるかは関係ありません:オブジェクトの中でも、そのプロトタイプ内でも。メソッド呼び出しでは、`this` は常にドットの前のオブジェクトです。** + +したがって、setter は実際に `this` として `admin` となり、`user` ではありません。 + +多くのメソッドを持つ大きなオブジェクトを持ち、それを継承する場合があるため、これは実際には非常に重要なことです。継承したオブジェクトで継承したメソッドを実行すると、大きなオブジェクトではなく、継承したオブジェクトの状態を変更します。 + +例えば、ここでは `animal` は "メソッド格納域" を表現しており、`rabbit` はそれを使います。 + +呼び出し `rabbit.sleep()` は `rabbit` オブジェクトに `this.isSleeping` をセットします。: + +```js run +// animal がメソッドを持っています +let animal = { + walk() { + if (!this.isSleeping) { + alert(`I walk`); + } + }, + sleep() { + this.isSleeping = true; + } +}; + +let rabbit = { + name: "White Rabbit", + __proto__: animal +}; + +// rabbit.isSleeping を変更する +rabbit.sleep(); + +alert(rabbit.isSleeping); // true +alert(animal.isSleeping); // undefined (prototype にそのようなプロパティはありません) +``` + +結果の図は次のようになります: + +![](proto-animal-rabbit-walk-3.svg) + +もし `bird`, `snake` など `animal` から継承された他のオブジェクトを持っていた場合、それらもまた `animal` のメソッドへのアクセスができます。しかし各メソッドでの `this` は対応するオブジェクトであり、`animal` ではなく、呼び出し時に評価されます(前のドット)。 したがって、`this` にデータを書き込むとき、これらのオブジェクトに格納されます。 + +結果として、メソッドは共有されますが、オブジェクトの状態は共有されません。 + +## for..in loop + +`for..in` ループは継承したプロパティも繰り返し処理します。 + +例: + +```js run +let animal = { + eats: true +}; + +let rabbit = { + jumps: true, + __proto__: animal +}; + +*!* +// Object.keys は実親のキーだけを返します +alert(Object.keys(rabbit)); // jumps +*/!* + +*!* +// for..in は実親と継承したキー両方をループします +for(let prop in rabbit) alert(prop); // jumps, eats +*/!* +``` + +これは期待している動作ではなく、継承したプロパティは除きたい場合は組み込みのメソッド [obj.hasOwnProperty(key)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty)が利用できます。これは `obj` が `key` という名前のプロパティをもつ場合(継承したものは含みません)、`true` を返します。 + +従って、継承したプロパティをフィルタする(あるいはそれらについて何かをする)ことが可能です。: + +```js run +let animal = { + eats: true +}; + +let rabbit = { + jumps: true, + __proto__: animal +}; + +for(let prop in rabbit) { + let isOwn = rabbit.hasOwnProperty(prop); + + if (isOwn) { + alert(`Our: ${prop}`); // Our: jumps + } else { + alert(`Inherited: ${prop}`); // Inherited: eats + } +} +``` + +ここでは、次の継承チェーンがあります: `rabbit` は `animal` を継承し、`animal` は `Object.prototype` を継承しています(`animal` はリテラルオブジェクト `{...}` なので、これはデフォルトです)。その上は `null` です。: + +![](rabbit-animal-object.svg) + +興味深い点が1つあります。メソッド `rabbit.hasOwnProperty` はどこから来るのでしょうか?我々は定義していません。チェーンをみることで、メソッドは `Object.prototype.hasOwnProperty` で提供されていることがわかります。つまり、継承されたものです。 + +...ですが、なぜ `hasOwnProperty` は継承したプロパティもリストする `for..in` ループで `eats` や `jumps` のように登場しないのでしょう。 + +答えはシンプルです。これは列挙不可だからです。`Object.prototype` の他のすべてのプロパティと同様に、`enumerable:false` フラグを持ちます。そして、`for..in` は列挙可能なプロパティのみをリストします。そのため、`Object.prototype` のプロパティは列挙されません。 + +```smart header="ほぼすべての他のキー/バリュー取得メソッドは継承したプロパティを無視します" +`Object.keys`, `Object.values` などのほとんどの他のキー/バリュー取得メソッドは、継承したプロパティを無視します。 + +これらはオブジェクト自身の操作のみ行います。プロトタイプからのプロパティは考慮され *ません*。 +``` + +## サマリ + +- JavaScriptでは、すべてのオブジェクトは隠れた `[[Prototype]]` プロパティを持っており、それは別のオブジェクトまたは `null` です。 +- それにアクセスするために `obj.__proto__` を使うことができます(他の方法もあります。それらは後ほど学びます)。 +- `[[Prototype]]` によるオブジェクトの参照は "プロトタイプ" と呼ばれます。 +- もしも `obj` のプロパティを読みたい、またはメソッドを呼び出したいが、そこに存在しない場合、JavaScriptはプロトタイプの中で見つけようとします。 +- 書き込み/削除操作はオブジェクトに対して直接動作し、プロトタイプを使いません(プロパティが setter でない限り)。 +- `obj.method()` を呼び出し、`method` がプロトタイプから取られた場合も、`this` は依然として `obj` を参照します。したがって、メソッドはたとえ継承されていたとしても、常に現在のオブジェクトで動作します。 +- `for..in` ループは自身と継承したプロパティ両方を繰り返し処理します。他のすべてのキー/バリュー取得メソッドはオブジェクト自身に対してのみ操作します。 diff --git a/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty.svg b/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty.svg new file mode 100644 index 0000000000..eb79c19ffd --- /dev/null +++ b/1-js/08-prototypes/01-prototype-inheritance/object-prototype-empty.svg @@ -0,0 +1 @@ +prototype objectobject[[Prototype]] \ No newline at end of file diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain.svg b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain.svg new file mode 100644 index 0000000000..4bf580ae77 --- /dev/null +++ b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-chain.svg @@ -0,0 +1 @@ +eats: true walk: functionanimaljumps: truerabbit[[Prototype]]earLength: 10longEar[[Prototype]] \ No newline at end of file diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2.svg b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2.svg new file mode 100644 index 0000000000..838c78395b --- /dev/null +++ b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-2.svg @@ -0,0 +1 @@ +eats: true walk: functionanimalwalk: functionrabbit[[Prototype]] \ No newline at end of file diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3.svg b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3.svg new file mode 100644 index 0000000000..d791e5390d --- /dev/null +++ b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk-3.svg @@ -0,0 +1 @@ +walk: function sleep: functionanimalrabbit[[Prototype]]name: "White Rabbit" isSleeping: true \ No newline at end of file diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk.svg b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk.svg new file mode 100644 index 0000000000..b324710286 --- /dev/null +++ b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit-walk.svg @@ -0,0 +1 @@ +eats: true walk: functionanimaljumps: truerabbit[[Prototype]] \ No newline at end of file diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit.svg b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit.svg new file mode 100644 index 0000000000..4f3c1bc0ec --- /dev/null +++ b/1-js/08-prototypes/01-prototype-inheritance/proto-animal-rabbit.svg @@ -0,0 +1 @@ +eats: trueanimaljumps: truerabbit[[Prototype]] \ No newline at end of file diff --git a/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin.svg b/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin.svg new file mode 100644 index 0000000000..bf0baf013a --- /dev/null +++ b/1-js/08-prototypes/01-prototype-inheritance/proto-user-admin.svg @@ -0,0 +1 @@ +name: "John" surname: "Smith" set fullName: functionisAdmin: true name: "Alice" surname: "Cooper"useradmin[[Prototype]] \ No newline at end of file diff --git a/1-js/08-prototypes/01-prototype-inheritance/rabbit-animal-object.svg b/1-js/08-prototypes/01-prototype-inheritance/rabbit-animal-object.svg new file mode 100644 index 0000000000..28daef3af8 --- /dev/null +++ b/1-js/08-prototypes/01-prototype-inheritance/rabbit-animal-object.svg @@ -0,0 +1,48 @@ + + + + rabbit-animal-object.svg + Created with sketchtool. + + + + + toString: function + hasOwnProperty: function + ... + + + Object.prototype + + + + animal + + + + [[Prototype]] + + + [[Prototype]] + + + + [[Prototype]] + + + null + + + eats: true + + + + rabbit + + + + jumps: true + + + + \ No newline at end of file diff --git a/1-js/07-object-oriented-programming/04-function-prototype/1-changing-prototype/solution.md b/1-js/08-prototypes/02-function-prototype/1-changing-prototype/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/1-changing-prototype/solution.md rename to 1-js/08-prototypes/02-function-prototype/1-changing-prototype/solution.md diff --git a/1-js/07-object-oriented-programming/04-function-prototype/1-changing-prototype/task.md b/1-js/08-prototypes/02-function-prototype/1-changing-prototype/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/1-changing-prototype/task.md rename to 1-js/08-prototypes/02-function-prototype/1-changing-prototype/task.md diff --git a/1-js/07-object-oriented-programming/04-function-prototype/4-new-object-same-constructor/solution.md b/1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/4-new-object-same-constructor/solution.md rename to 1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md diff --git a/1-js/07-object-oriented-programming/04-function-prototype/4-new-object-same-constructor/task.md b/1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/04-function-prototype/4-new-object-same-constructor/task.md rename to 1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/task.md diff --git a/1-js/08-prototypes/02-function-prototype/article.md b/1-js/08-prototypes/02-function-prototype/article.md new file mode 100644 index 0000000000..f4593cda9d --- /dev/null +++ b/1-js/08-prototypes/02-function-prototype/article.md @@ -0,0 +1,175 @@ +# F.prototype + +思い出してください、新しいオブジェクトは `new F()` のように、コンストラクタ関数で生成できます。 + +`F.prototype` がオブジェクトの場合、`new` 演算子は新しいオブジェクトで `[[Prototype]]` をセットするためにそれを使用します。 + +```smart +JavaScriptは最初からプロトタイプの継承を持っています。 それは言語の中心的な特徴の1つでした。 + +しかし、昔は直接アクセスすることはできませんでした。確実に機能したのは、この章で説明する、コンストラクタ関数の `"prototype"` プロパティを使うことです。そして、それを使っているスクリプトはまだたくさんあります。 +``` + +ここで `F.prototype` は `F` 上の `"prototype"` と名付けられた通常のプロパティを意味していることに注意してください。用語 "プロトタイプ" と似ていますが、ここでは本当にその名前をもつ通常のプロパティを意味しています。 + +ここではその例です: + +```js run +let animal = { + eats: true +}; + +function Rabbit(name) { + this.name = name; +} + +*!* +Rabbit.prototype = animal; +*/!* + +let rabbit = new Rabbit("White Rabbit"); // rabbit.__proto__ == animal + +alert( rabbit.eats ); // true +``` + +`Rabbit.prototype = animal` の設定は、文字通り次のことを述べています。: "`new Rabbit` が生成される時、その `[[Prototype]]` へ `animal` を割り当てます。" + +これが結果のイメージです: + +![](proto-constructor-animal-rabbit.svg) + +上記の図で、`"prototype"` は水平矢印で、通常のプロパティです。`[[Prototype]]` は縦矢印で、`animal` から `rabbit` の継承を意味しています。 + +```smart header="`F.prototype` は `new F` 時にだけ使用されます" +`F.prototype` プロパティは `new F` が呼ばれたときにだけ使用され、新しいオブジェクトの `[[Prototype]]` を割り当てます。 + +作成後に、`F.prototype` プロパティが変更された場合(`F.prototype = <別のオブジェクト>`)、`new F` によって生成された新しいオブジェクトは `[[Prototype]]` として別のオブジェクトを持ちますが、既に存在するオブジェクトは古いものを保持したままです。 +``` + +## デフォルトの F.prototype, constructor プロパティ + +すべての関数は、たとえ明示的に提供されていなくても `"prototype"` プロパティを持っています。 + +デフォルトの `"prototype"` は `constructor` というプロパティだけを持つオブジェクトで、それは関数自体を指します。 + +こんな感じです: + +```js +function Rabbit() {} + +/* デフォルト prototype +Rabbit.prototype = { constructor: Rabbit }; +*/ +``` + +![](function-prototype-constructor.svg) + +コードでそれを確認できます: + +```js run +function Rabbit() {} +// デフォルトでは: +// Rabbit.prototype = { constructor: Rabbit } + +alert( Rabbit.prototype.constructor == Rabbit ); // true +``` + +当然、何もしない場合、 `constructor` プロパティは `[[Prototype]]` を通じてすべての rabbit が利用できます。: + +```js run +function Rabbit() {} +// デフォルトでは: +// Rabbit.prototype = { constructor: Rabbit } + +let rabbit = new Rabbit(); // {constructor: Rabbit} の継承 + +alert(rabbit.constructor == Rabbit); // true (prototype から) +``` + +![](rabbit-prototype-constructor.svg) + +`constructor` プロパティを使って既存のものと同じコンストラクタを使って新しいオブジェクトを作成することができます。 + +このように: + +```js run +function Rabbit(name) { + this.name = name; + alert(name); +} + +let rabbit = new Rabbit("White Rabbit"); + +*!* +let rabbit2 = new rabbit.constructor("Black Rabbit"); +*/!* +``` + +これは、オブジェクトを持っているが、どのコンストラクタが使われたか分からない場合(例えばサードパーティーのライブラリが使われているなど)で、同じ種類のものを使って別のオブジェクトを作る必要がある場合に便利です。 + +しかし、おそらく `"constructor"` に関する最も重要なことは... + +**...JavaScript 自体は正しい `"constructor"` 値を保証しません。** + +はい、関数のためのデフォルトの `"prototype"` は存在しますが、それがすべてです。その後どうなるかは私たち次第です。 + +特に、もしデフォルトプロトタイプ全体を置き換えると、その中に `"constructor"` はなくなります。 + +例: + +```js run +function Rabbit() {} +Rabbit.prototype = { + jumps: true +}; + +let rabbit = new Rabbit(); +*!* +alert(rabbit.constructor === Rabbit); // false +*/!* +``` + +したがって、正しい `"constructor"` を維持するためには、全体を上書きする代わりに、デフォルト `"prototype"` に対して追加/削除を行います。: + +```js +function Rabbit() {} + +// 完全に Rabbit.prototype を上書きはしません +// 単に追加するだけです +Rabbit.prototype.jumps = true +// デフォルト Rabbit.prototype.constructor は保持されます +``` + +もしくは、代替として手動で `constructor` プロパティを再び作ります。: + +```js +Rabbit.prototype = { + jumps: true, +*!* + constructor: Rabbit +*/!* +}; + +// 追加したので、これで constructor も正しいです +``` + + +## サマリ + +このチャプターでは、constructor 関数を通して作成されたオブジェクトのための `[[Prototype]]` を設定方法について簡単に説明しました。後で、それに依存するより高度なプログラミングパターンを見ていきます。 + +すべてが非常にシンプルで、物事を明確にするための留意事項はほんの少しです。: + +- `F.prototype` プロパティは `[[Prototype]]` と同じではありません。`F.prototype` がする唯一のことは: `new F()` が呼ばれたときに新しいオブジェクトの `[[Prototype]]` をセットすることです。 +- `F.prototype` の値はオブジェクトまたは null でなければなりません。: 他の値では動作しません。 +- `"prototype"` プロパティはコンストラクタ関数に設定され、`new` で呼び出されたときにのみ、特別な効果があります。 + +通常のオブジェクトでは、`prototype` は特別なものではありません。: +```js +let user = { + name: "John", + prototype: "Bla-bla" // no magic at all +}; +``` + +デフォルトでは、すべての関数は `F.prototype = { constructor: F }` を持っているので、その `"constructor"` プロパティへアクセスすることで、オブジェクトの constructor を取得することができます。 diff --git a/1-js/08-prototypes/02-function-prototype/function-prototype-constructor.svg b/1-js/08-prototypes/02-function-prototype/function-prototype-constructor.svg new file mode 100644 index 0000000000..59d60b397a --- /dev/null +++ b/1-js/08-prototypes/02-function-prototype/function-prototype-constructor.svg @@ -0,0 +1 @@ +Rabbitprototypeconstructordefault "prototype" \ No newline at end of file diff --git a/1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring.svg b/1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring.svg new file mode 100644 index 0000000000..37601da27b --- /dev/null +++ b/1-js/08-prototypes/02-function-prototype/native-prototypes-array-tostring.svg @@ -0,0 +1,39 @@ + + + + native-prototypes-array-tostring.svg + Created with sketchtool. + + + + + toString: function + + ... + + + Array.prototype + + + + toString: function + ... + + + Object.prototype + + + + + [[Prototype]] + + + + [[Prototype]] + + + [1, 2, 3] + + + + \ No newline at end of file diff --git a/1-js/08-prototypes/02-function-prototype/native-prototypes-classes.svg b/1-js/08-prototypes/02-function-prototype/native-prototypes-classes.svg new file mode 100644 index 0000000000..a6f78e5747 --- /dev/null +++ b/1-js/08-prototypes/02-function-prototype/native-prototypes-classes.svg @@ -0,0 +1,87 @@ + + + + native-prototypes-classes.svg + Created with sketchtool. + + + + + toString: function + other object methods + + + Object.prototype + + + + + null + + + + slice: function + other array methods + + + [[Prototype]] + + + [[Prototype]] + + + [[Prototype]] + + + [[Prototype]] + + + [[Prototype]] + + + [[Prototype]] + + + [[Prototype]] + + + Array.prototype + + + + call: function + other function methods + + + Function.prototype + + + + toFixed: function + other number methods + + + Number.prototype + + + + + + [1, 2, 3] + + + + function f(args) { + ... + } + + + + 5 + + + + + + + \ No newline at end of file diff --git a/1-js/08-prototypes/02-function-prototype/object-prototype-1.svg b/1-js/08-prototypes/02-function-prototype/object-prototype-1.svg new file mode 100644 index 0000000000..8a93d2a864 --- /dev/null +++ b/1-js/08-prototypes/02-function-prototype/object-prototype-1.svg @@ -0,0 +1,38 @@ + + + + object-prototype-1.svg + Created with sketchtool. + + + + + constructor: Object + toString: function + ... + + + + Object.prototype + + + + Object + + + obj = new Object() + + + + + [[Prototype]] + + + prototype + + + constructor + + + + \ No newline at end of file diff --git a/1-js/08-prototypes/02-function-prototype/object-prototype.svg b/1-js/08-prototypes/02-function-prototype/object-prototype.svg new file mode 100644 index 0000000000..828f61d3be --- /dev/null +++ b/1-js/08-prototypes/02-function-prototype/object-prototype.svg @@ -0,0 +1,30 @@ + + + + object-prototype.svg + Created with sketchtool. + + + + + constructor: Object + toString: function + ... + + + Object.prototype + + + + Object + + + + prototype + + + constructor + + + + \ No newline at end of file diff --git a/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit.svg b/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit.svg new file mode 100644 index 0000000000..ede4e1227e --- /dev/null +++ b/1-js/08-prototypes/02-function-prototype/proto-constructor-animal-rabbit.svg @@ -0,0 +1 @@ +eats: truename: "White Rabbit"animalRabbitrabbit[[Prototype]]prototype \ No newline at end of file diff --git a/1-js/08-prototypes/02-function-prototype/rabbit-animal-object.svg b/1-js/08-prototypes/02-function-prototype/rabbit-animal-object.svg new file mode 100644 index 0000000000..28daef3af8 --- /dev/null +++ b/1-js/08-prototypes/02-function-prototype/rabbit-animal-object.svg @@ -0,0 +1,48 @@ + + + + rabbit-animal-object.svg + Created with sketchtool. + + + + + toString: function + hasOwnProperty: function + ... + + + Object.prototype + + + + animal + + + + [[Prototype]] + + + [[Prototype]] + + + + [[Prototype]] + + + null + + + eats: true + + + + rabbit + + + + jumps: true + + + + \ No newline at end of file diff --git a/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor.svg b/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor.svg new file mode 100644 index 0000000000..54b3d79804 --- /dev/null +++ b/1-js/08-prototypes/02-function-prototype/rabbit-prototype-constructor.svg @@ -0,0 +1 @@ +default "prototype"Rabbitrabbit[[Prototype]]prototypeconstructor \ No newline at end of file diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/1-defer-to-prototype/solution.md b/1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/1-defer-to-prototype/solution.md rename to 1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/solution.md diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/1-defer-to-prototype/task.md b/1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/1-defer-to-prototype/task.md rename to 1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/task.md diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/2-defer-to-prototype-extended/solution.md b/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/2-defer-to-prototype-extended/solution.md rename to 1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/solution.md diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/2-defer-to-prototype-extended/task.md b/1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/2-defer-to-prototype-extended/task.md rename to 1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/task.md diff --git a/1-js/08-prototypes/03-native-prototypes/article.md b/1-js/08-prototypes/03-native-prototypes/article.md new file mode 100644 index 0000000000..d9b3167af7 --- /dev/null +++ b/1-js/08-prototypes/03-native-prototypes/article.md @@ -0,0 +1,198 @@ +# ネイティブのプロトタイプ + +`"prototype"` プロパティはJavaScript自身のコア部分で広く使われています。すべての組み込みのコンストラクタ関数はこれを使用しています。 + +最初に単純なオブジェクトの場合を見ていき、次により複雑なオブジェクトの場合にどのようになるかを見ていきましょう。 + +## Object.prototype + +空のオブジェクトを出力してみましょう。: + +```js run +let obj = {}; +alert( obj ); // "[object Object]" ? +``` + +文字列 `"[object Object]"` を生成するコードはどこにあるのでしょう? それは組み込みの `toString` メソッドですが、どこにあるのでしょう? `obj` は空です! + +...しかし、短い記法 `obj = {}` は `obj = new Object()` と同じで、その `Object` は組み込みのオブジェクトコンストラクタ関数です。その関数は `toString` や他の関数を持つ巨大なオブジェクトを参照する `Object.prototype` を持っています。 + +このようになります: + +![](object-prototype.svg) + +`new Object()` が呼ばれた(もしくはリテラルオブジェクト `{...}` が作られた)とき、その `[[Prototype]]` は前のチャプターで私たちが話してきたルールによって、 `Object.prototype` にセットされます。: + +![](object-prototype-1.svg) + +その後、`obj.toString()` が呼ばれると、`Object.prototype` からメソッドが取り出されます。 + +このようにして確認できます: + +```js run +let obj = {}; + +alert(obj.__proto__ === Object.prototype); // true + +alert(obj.toString === obj.__proto__.toString); //true +alert(obj.toString === Object.prototype.toString); //true +``` + +上の `Object.prototype` のチェーンで、追加の `[[Prototype]]` がないことに注意してください。: + +```js run +alert(Object.prototype.__proto__); // null +``` + +## 他の組み込みのプロトタイプ + +`Array`, `Date`, `Function` のような、他の組み込みのオブジェクトもまたプロトタイプにメソッドを保持しています。 + +例えば、配列 `[1, 2, 3]` を作るとき、デフォルトの `new Array()` コンストラクタが内部で使われます。なので、配列データは新しいオブジェクトに書き込まれ、`Array.prototype` はそのプロトタイプとなり、メソッドを提供します。これは非常にメモリ効率が良いです。 + +スペックでは、すべての組み込みのプロトタイプは先頭に `Object.prototype` を持っています。なので、"すべてはオブジェクトを継承している" という人もいます。 + +全体図は次のとおりです(3つの組み込みについて書いています): + +![](native-prototypes-classes.svg) + +プロトタイプを手動でチェックしてみましょう。: + +```js run +let arr = [1, 2, 3]; + +// Array.prototype から継承している? +alert( arr.__proto__ === Array.prototype ); // true + +// 次に Object.prototype からは継承している? +alert( arr.__proto__.__proto__ === Object.prototype ); // true + +// そしてトップの null +alert( arr.__proto__.__proto__.__proto__ ); // null +``` + +プロトタイプのメソッドのいくつかは重複する可能性があります。例えば、`Array.prototype` はカンマ区切りで要素を表示する自身の `toString` を持っています。: + +```js run +let arr = [1, 2, 3] +alert(arr); // 1,2,3 <-- Array.prototype.toString の結果 +``` + +以前見たように、`Object.prototype` も同様に `toString` を持っていますが、`Array.prototype` はチェーンでより近い位置にあるので、配列がもつものが使用されます。 + + +![](native-prototypes-array-tostring.svg) + + +Chrome developer console のようなブラウザ内のツールでも継承を表示できます(組み込みオブジェクトのために `console.dir` を使う必要があるかもしれません)。 + +![](console_dir_array.png) + +他の組み込みオブジェクトも同じように動作します。関数でさえも、それらは組み込みの `Function` コンストラクタのオブジェクトであり、メソッドです: `call/apply` など、`Function.prototype` から取り出されたものです。関数には独自の `toString`もあります。 + +```js run +function f() {} + +alert(f.__proto__ == Function.prototype); // true +alert(f.__proto__.__proto__ == Object.prototype); // true, object からの継承 +``` + +## プリミティブ(Primitives) + +最も複雑なことは、文字列、数値、ブール値で起こります。 + +覚えている通り、それらはオブジェクトではありません。しかし、それらのプロパティへアクセスをしようとした場合、組み込みのコンストラクタ `String`, `Number`, `Boolean` を使った一時的なラッパーオブジェクトが作られます。それらはメソッドを提供し、消えます。 + +それらのオブジェクトは我々には見えない形で作られ、ほとんどのエンジンはそれらを最適化しますが、スペックではこのように正確に説明されています。それらのオブジェクトのメソッドもまた `String.prototype`, `Number.prototype` や `Boolean.prototype` として利用可能なものとしてプロトタイプに存在します。 + +```warn header="値 `null` と `undefined` はオブジェクトラッパーを持っていません" +特別な値 `null` や `undefined` は別です。それらはオブジェクトラッパーを持ちません。そのため、利用可能なメソッドやプロパティはありません。また、それらに対応するプロトタイプもありません。 +``` + +## ネイティブプロトタイプの変更 + +ネイティブプロトタイプは変更することができます。例えば、もしあるメソッドを `String.prototype` に追加した場合、それはすべての文字列で利用可能になります。: + +```js run +String.prototype.show = function() { + alert(this); +}; + +"BOOM!".show(); // BOOM! +``` + +開発の過程で、新しい組み込みメソッドを追加したいと考えてるかもしれません。そして、それをネイティブプロトタイプに加えたいという、若干の誘惑があるかもしれません。ですが、それは一般的には悪い考えです。 + +``` +プロトタイプはグローバルなので、コンフリクトが発生しやすいです。もし2つのライブラリがメソッド `String.prototype.show` を追加している場合、片方はもう一方に上書きされます。 + +そのため、一般的には、ネイティブのプロトタイプの変更は悪いアイデアとされています。 +``` + +**現代のプログラミングでは、ネイティブプロトタイプの変更が認められたケースが1つだけあります。それはポリフィルです。** + +ポリフィルは JavaScript のスペックには存在するが、特定のJavaScript エンジンではまだサポートされていないメソッドの代わりを用意することを指す用語です。 + +手動で実装し、組み込みのプロトタイプにそれを取り込むことができます。 + +例: + +```js run +if (!String.prototype.repeat) { // もしこのようなメソッドがない場合 + // prototype に追加します + + String.prototype.repeat = function(n) { + // 文字列を n 回繰り返す + + // 実際、このコードはこれより複雑になります + // "n" の負の値に対するエラーのスロー + // 完全なアルゴリズムは仕様にあります + return new Array(n + 1).join(this); + }; +} + +alert( "La".repeat(3) ); // LaLaLa +``` + + +## プロトタイプからの借用 + +チャプター で私たちはメソッドの借用について話しました。: + +これはあるオブジェクトからメソッドをとり、それを別のオブジェクトにコピーするときです。 + +ネイティブプロトタイプのメソッドのいくつかはしばしば借用されます。 + +例えば、配列ライクなオブジェクトを作成している場合、そこにいくつかの `Array` メソッドをそこにコピーしたい場合があります。 + +例 + +```js run +let obj = { + 0: "Hello", + 1: "world!", + length: 2, +}; + +*!* +obj.join = Array.prototype.join; +*/!* + +alert( obj.join(',') ); // Hello,world! +``` + +組み込みの `join` メソッドの内部アルゴリズムは、正しいインデックスと `length` プロパティのみを考慮するため、これは機能します。オブジェクトがたしかに配列かどうかはチェックしません。多くの組み込みメソッドはこのようになっています。 + +別の可能性は、`obj.__proto__` を `Array.prototype` に設定することで継承することで、これによりすべての `Array` メソッドは自動的に `obj` で利用可能になります。 + +ですが、`obj` が既に別のオブジェクトを継承していた場合にはこれは不可能です。一度に1つのオブジェクトしか継承できません。 + +借用メソッドは柔軟であり、必要であれば異なるオブジェクトから機能を混ぜることも可能です。 + +## サマリ + +- すべての組み込みオブジェクトは同じパターンに従います。: + - メソッドはプロトタイプに保持されています(`Array.prototype`, `Object.prototype`, `Date.prototype` など)。 + - オブジェクト自身はデータのみを保持します(配列アイテム、オブジェクトプロパティ、日付)。 +- プリミティブもまたラッパーオブジェクトのプロトタイプにメソッドを保持します。: `Number.prototype`, `String.prototype`, `Boolean.prototype` 。`undefined` と `null` にだけはラッパーオブジェクトはありません。 +- 組み込みのプロトタイプを変更したり、新しいメソッドを実装することができます。 しかし、それを変更することはお勧めしません。 おそらく唯一許可されるケースは、新しい標準が追加されたものの、まだエンジンのJavaScriptメソッドではサポートされていないときだけです。 diff --git a/1-js/07-object-oriented-programming/05-native-prototypes/console_dir_array.png b/1-js/08-prototypes/03-native-prototypes/console_dir_array.png similarity index 100% rename from 1-js/07-object-oriented-programming/05-native-prototypes/console_dir_array.png rename to 1-js/08-prototypes/03-native-prototypes/console_dir_array.png diff --git a/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor.svg b/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor.svg new file mode 100644 index 0000000000..59d60b397a --- /dev/null +++ b/1-js/08-prototypes/03-native-prototypes/function-prototype-constructor.svg @@ -0,0 +1 @@ +Rabbitprototypeconstructordefault "prototype" \ No newline at end of file diff --git a/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring.svg b/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring.svg new file mode 100644 index 0000000000..ebb4f32051 --- /dev/null +++ b/1-js/08-prototypes/03-native-prototypes/native-prototypes-array-tostring.svg @@ -0,0 +1 @@ +toString: function ...Array.prototypetoString: function ...Object.prototype[[Prototype]][[Prototype]][1, 2, 3] \ No newline at end of file diff --git a/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.svg b/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.svg new file mode 100644 index 0000000000..4d6129e0a0 --- /dev/null +++ b/1-js/08-prototypes/03-native-prototypes/native-prototypes-classes.svg @@ -0,0 +1 @@ +toString: function other object methodsObject.prototypenullslice: function other array methods[[Prototype]][[Prototype]][[Prototype]][[Prototype]][[Prototype]][[Prototype]][[Prototype]]Array.prototypecall: function other function methodsFunction.prototypetoFixed: function other number methodsNumber.prototype[1, 2, 3]function f(args) { ... }5 \ No newline at end of file diff --git a/1-js/08-prototypes/03-native-prototypes/object-prototype-1.svg b/1-js/08-prototypes/03-native-prototypes/object-prototype-1.svg new file mode 100644 index 0000000000..9630e68e27 --- /dev/null +++ b/1-js/08-prototypes/03-native-prototypes/object-prototype-1.svg @@ -0,0 +1 @@ +constructor: Object toString: function ...Object.prototypeObjectobj = new Object()[[Prototype]]prototype \ No newline at end of file diff --git a/1-js/08-prototypes/03-native-prototypes/object-prototype-null.svg b/1-js/08-prototypes/03-native-prototypes/object-prototype-null.svg new file mode 100644 index 0000000000..9ccb342299 --- /dev/null +++ b/1-js/08-prototypes/03-native-prototypes/object-prototype-null.svg @@ -0,0 +1 @@ +obj[[Prototype]]null \ No newline at end of file diff --git a/1-js/08-prototypes/03-native-prototypes/object-prototype.svg b/1-js/08-prototypes/03-native-prototypes/object-prototype.svg new file mode 100644 index 0000000000..024dd30213 --- /dev/null +++ b/1-js/08-prototypes/03-native-prototypes/object-prototype.svg @@ -0,0 +1 @@ +constructor: Object toString: function ...Object.prototypeObjectprototype \ No newline at end of file diff --git a/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit.svg b/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit.svg new file mode 100644 index 0000000000..ede4e1227e --- /dev/null +++ b/1-js/08-prototypes/03-native-prototypes/proto-constructor-animal-rabbit.svg @@ -0,0 +1 @@ +eats: truename: "White Rabbit"animalRabbitrabbit[[Prototype]]prototype \ No newline at end of file diff --git a/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor.svg b/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor.svg new file mode 100644 index 0000000000..54b3d79804 --- /dev/null +++ b/1-js/08-prototypes/03-native-prototypes/rabbit-prototype-constructor.svg @@ -0,0 +1 @@ +default "prototype"Rabbitrabbit[[Prototype]]prototypeconstructor \ No newline at end of file diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/2-dictionary-tostring/solution.md b/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/06-prototype-methods/2-dictionary-tostring/solution.md rename to 1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/2-dictionary-tostring/task.md b/1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/06-prototype-methods/2-dictionary-tostring/task.md rename to 1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/task.md diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/solution.md b/1-js/08-prototypes/04-prototype-methods/3-compare-calls/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/solution.md rename to 1-js/08-prototypes/04-prototype-methods/3-compare-calls/solution.md diff --git a/1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/task.md b/1-js/08-prototypes/04-prototype-methods/3-compare-calls/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/06-prototype-methods/3-compare-calls/task.md rename to 1-js/08-prototypes/04-prototype-methods/3-compare-calls/task.md diff --git a/1-js/08-prototypes/04-prototype-methods/article.md b/1-js/08-prototypes/04-prototype-methods/article.md new file mode 100644 index 0000000000..651f70f0fa --- /dev/null +++ b/1-js/08-prototypes/04-prototype-methods/article.md @@ -0,0 +1,204 @@ + +# __proto__ なしでの プロトタイプ メソッド, オブジェクト + +このセクションの最初の最初の章では、プロトタイプ をセットアップする現代のメソッドを説明します。 + +`__proto__` は古く、やや非推奨とされています(JavaScript標準のブラウザのみの部分で)。 + +現代のメソッドは次のものです: + +- [Object.create(proto[, descriptors])](mdn:js/Object/create) -- 与えられた `proto` を `[[Prototype]]` として、また任意のプロパティディスクリプタで空のオブジェクトを作ります。 +- [Object.getPrototypeOf(obj)](mdn:js/Object.getPrototypeOf) -- `obj` の `[[Prototype]]` を返します。 +- [Object.setPrototypeOf(obj, proto)](mdn:js/Object.setPrototypeOf) -- `obj` の `[[Prototype]]` に `proto` をセットします。 + +これらは `__proto__` の代わりに使用します。 + +例: + +```js run +let animal = { + eats: true +}; + +// animal をプロトタイプとして新しいオブジェクトを作成する +*!* +let rabbit = Object.create(animal); +*/!* + +alert(rabbit.eats); // true + +*!* +alert(Object.getPrototypeOf(rabbit) === animal); // rabbit のプロトタイプを取得 +*/!* + +*!* +Object.setPrototypeOf(rabbit, {}); // rabbit のプロトタイプを {} に変更 +*/!* +``` + +`Object.create` は任意で2つ目の引数を持ち、これはプロパティディスクリプタです。次のように、新しいオブジェクトに追加のプロパティが指定できます : + +```js run +let animal = { + eats: true +}; + +let rabbit = Object.create(animal, { + jumps: { + value: true + } +}); + +alert(rabbit.jumps); // true +``` + +ディスクリプタはチャプター で説明したのと同じフォーマットです。 + +`for..in` でプロパティをコピーするよりも、より強力にオブジェクトをクローンするのに `Object.create` が使用できます。: + +```js +let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); +``` + +この呼び出しはすべてのプロパティを含む `obj` の本当の正確なコピーを作ります。: 列挙型、非列挙型、データプロパティ、setter/getter -- すべてと、正確な `[[Prototype]]` です。 + +## 略史 + +`[[Prototype]]` を管理する方法はたくさんあります! 同じことをするたくさんの方法があります! + +なぜでしょう? + +それは歴史的な理由によるものです。 + +- コンストラクタ関数の `"prototype"` プロパティはとても古くから機能しています。 +- 2012年後半: `Object.create` が標準に登場しました。これは与えられたプロトタイプでオブジェクトを作りますが、それを取得/設定することはできませんでした。そのため、ブラウザはいつでもプロトタイプの取得/設定ができる非標準の `__proto__` アクセサを実装しました。 +- 2015年後半: `Object.setPrototypeOf` と `Object.getPrototypeOf` が標準に追加されました。`__proto__` はどこでも実行されているデファクトであったため、標準の付録Bに追加されています。これはブラウザ以外の環境ではオプションです。 + +今現在、私たちはこれらのすべての方法を自由に使うことができます。 + +なぜ `__proto__` は関数 `getPrototypeOf/setPrototypeOf` に置き換えられたのでしょうか?これは興味深い質問であり、なぜ `__proto__` ではダメなのかを理解する必要があります。答えを知るには続きを読んでください。 + +```warn header="速度が重要な場合は、既存のオブジェクトの `[[Prototype]]` を変更しないでください" +技術的には、いつでも `[[Prototype]]` の取得/設定が可能です。しかし、通常はオブジェクト作成時に一度だけ設定を行い、変更はしません。`rabbit` は `animal` から継承しており、それは変更しません。 + +また、JavaScriptエンジンは高度に最適化されています。`Object.setPrototypeOf` または `obj.__proto__=` で "その場で" プロトタイプを変更することは、可能ですがとても遅い操作になります。そのため、何をしようとしているかわかっている場合、または JavaScript の速度が全く問題にならない場合以外は避けてください。 +``` + +## "非常にシンプルな" オブジェクト + +ご存知の通り、オブジェクトはキー/値ペアを格納するための連想配列として使うことができます。 + +...しかし、もしその中で *ユーザから提供された* キーを格納しようとした場合(例えばユーザが入力した辞書)、興味深い問題が起こります。: すべてのキーは `"__proto__"` を除いてうまく動作します。 + +例を確認してみましょう: + +```js run +let obj = {}; + +let key = prompt("What's the key?", "__proto__"); +obj[key] = "some value"; + +alert(obj[key]); // [object Object], "some value" ではありません! +``` + +ここでは、ユーザが `__proto__` を入力した場合、その代入は無視されます! + +それは驚くことではありません。`__proto__` プロパティは特別です: それはオブジェクトまたは `null` であり、文字列はプロトタイプにはなれません。 + +しかし、このような振る舞いを実装するつもりはありませんでした。私たちはキー/値ペアを格納したいですが、キー名が `"__proto__"` の場合は正しく保存されませんでした。なので、これはバグです。 + +ここでの結果はひどくはありませんが、他のケースでは、プロトタイプは実際に変更される可能性があるため、処理が予期しない方向で間違ってしまう可能性があります。 + +最悪なのは、通常開発者はこのような可能性について全く考えません。そのため、このようなバグに気付きにくくなり、特にJavaScriptがサーバー側で使用されている場合には、それらを脆弱性に変えることさえあります。 + +また、デフォルトの関数である、`toString` や他の組み込みメソッドに代入する場合も予期しないことが起こる可能性があります。 + +どうやってこの問題を回避しましょう? + +まず、通常のオブジェクトの代わりに、格納域として `Map` を使用するよう切り替える方法があります。それですべて問題ありません。 + +ですが、`Object` もまたここでは上手く機能します + +`__proto__` はオブジェクトのプロパティではなく、`Object.prototype` のアクセサプロパティです。: + +![](object-prototype-2.svg) + +なので、もし `obj.__proto__` が読み込まれたり代入された場合、該当の getter/setter がそのプロトタイプから呼ばれ、それは `[[Prototype]]` の取得/設定をします。 + +最初に言ったとおり、`__proto__` は `[[Prototype]]` にアクセスする方法であり、`[[Prototype]]` 自身ではありません。 + +今、連想配列としてオブジェクトを使いたい場合、リテラルのトリックを使ってそれを行う事ができます。: + +```js run +*!* +let obj = Object.create(null); +*/!* + +let key = prompt("What's the key?", "__proto__"); +obj[key] = "some value"; + +alert(obj[key]); // "some value" +``` + +`Object.create(null)` はプロトタイプなし(`[[Prototype]]` が `null`)の空オブジェクトを作ります。: + +![](object-prototype-null.svg) + +したがって、`__proto__` に対する継承された getter/setter はありません。今や通常のデータプロパティとして処理されますので、上の例は正しく動作します。 + +このようなオブジェクトを "非常にシンプルな" または "純粋な辞書オブジェクト" と呼びます。なぜなら、それらは通常のオブジェクト `{...}` よりもシンプルなためです。 + +欠点は、そのようなオブジェクトには組み込みのオブジェクトメソッドがないことです。 `toString` など: + +```js run +*!* +let obj = Object.create(null); +*/!* + +alert(obj); // Error (no toString) +``` + +...しかし、連想配列ではそれは通常問題ありません。 + +ほとんどのオブジェクトに関連したメソッドは `Object.keys(obj)` のように `Object.something(...)` であることに注目してください。それらはプロトタイプにはないので、このようなオブジェクトで機能し続けます。: + + +```js run +let chineseDictionary = Object.create(null); +chineseDictionary.hello = "ni hao"; +chineseDictionary.bye = "zai jian"; + +alert(Object.keys(chineseDictionary)); // hello,bye +``` + +## サマリ + +プロトタイプをセットアップしたり直接アクセスするための現代のメソッドは次の通りです: + +- [Object.create(proto[, descriptors])](mdn:js/Object/create) -- 与えられた `proto` を `[[Prototype]]` (`null` もOK)として、また任意のプロパティディスクリプタで空のオブジェクトを作ります。 +- [Object.getPrototypeOf(obj)](mdn:js/Object.getPrototypeOf) -- `obj` の `[[Prototype]]` を返します( `__proto__` の getter と同じです)。 +- [Object.setPrototypeOf(obj, proto)](mdn:js/Object.setPrototypeOf) -- `obj` の `[[Prototype]]` に `proto` をセットします( `__proto__` の setter と同じです)。 + +組み込みの `__proto__` の getter/setter はユーザが作成したキーをオブジェクトに入れる場合に安全ではありません。ユーザがキーとして `"__proto__"` を入力する可能性があり、その場合エラーが発生し、通常は予期せぬ結果になるでしょう。 + +なので、`Object.create(null)` を使用して `__proto__` をもたない "非常にシンプルな" オブジェクトを作成するか、`Map` オブジェクトを使用します。 + +また、`Object.create` はオブジェクトのすべてのディスクリプタの浅いコピー(shallow-copy)を作成する簡単な方法です。 + +```js +let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); +``` + +さらに、`__proto__` は `[[Prototype]]` の getter/setterであり、他のメソッドと同様に `Object.prototype` に存在することも明らかにしました。 + +私たちは、`Object.create(null)` によってプロトタイプなしのオブジェクトを作ることができます。このようなオブジェクトは "純粋な辞書" として使われ、キーとして `"__proto__"` の問題はありません。 + +他のメソッドです: + +- [Object.keys(obj)](mdn:js/Object/keys) / [Object.values(obj)](mdn:js/Object/values) / [Object.entries(obj)](mdn:js/Object/entries) -- 列挙可能な自身の文字列プロパティ名/値/キー値ペアの配列を返します。 +- [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) -- すべての自身のシンボリックプロパティ名の配列を返します。 +- [Object.getOwnPropertyNames(obj)](mdn:js/Object/getOwnPropertyNames) -- すべての自身の文字列プロパティ名を返します。 +- [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) -- すべての自身のプロパティ名の配列を返します。 +- [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty): それは `obj` 自身が `key` という名前のプロパティをもつ(継承でない) 場合に `true` を返します。 + +オブジェクトプロパティ(`Object.keys` など)を返すすべてのメソッドは "自身の" プロパティを返します。もし継承されたものが欲しい場合は、`for..in` を使います。 diff --git a/1-js/08-prototypes/04-prototype-methods/object-prototype-2.svg b/1-js/08-prototypes/04-prototype-methods/object-prototype-2.svg new file mode 100644 index 0000000000..cf4d3023f8 --- /dev/null +++ b/1-js/08-prototypes/04-prototype-methods/object-prototype-2.svg @@ -0,0 +1 @@ +... get __proto__: function set __proto__: functionObject.prototypeObjectobj[[Prototype]]prototype \ No newline at end of file diff --git a/1-js/08-prototypes/04-prototype-methods/object-prototype-null.svg b/1-js/08-prototypes/04-prototype-methods/object-prototype-null.svg new file mode 100644 index 0000000000..9ccb342299 --- /dev/null +++ b/1-js/08-prototypes/04-prototype-methods/object-prototype-null.svg @@ -0,0 +1 @@ +obj[[Prototype]]null \ No newline at end of file diff --git a/1-js/08-prototypes/04-prototype-methods/rabbit-animal-object.svg b/1-js/08-prototypes/04-prototype-methods/rabbit-animal-object.svg new file mode 100644 index 0000000000..28daef3af8 --- /dev/null +++ b/1-js/08-prototypes/04-prototype-methods/rabbit-animal-object.svg @@ -0,0 +1,48 @@ + + + + rabbit-animal-object.svg + Created with sketchtool. + + + + + toString: function + hasOwnProperty: function + ... + + + Object.prototype + + + + animal + + + + [[Prototype]] + + + [[Prototype]] + + + + [[Prototype]] + + + null + + + eats: true + + + + rabbit + + + + jumps: true + + + + \ No newline at end of file diff --git a/1-js/08-prototypes/index.md b/1-js/08-prototypes/index.md new file mode 100644 index 0000000000..3943c1af9f --- /dev/null +++ b/1-js/08-prototypes/index.md @@ -0,0 +1 @@ +# プロトタイプ, 継承 diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.md b/1-js/09-classes/01-class/1-rewrite-to-class/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.md rename to 1-js/09-classes/01-class/1-rewrite-to-class/solution.md diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/clock.js b/1-js/09-classes/01-class/1-rewrite-to-class/solution.view/clock.js similarity index 100% rename from 1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/solution.view/clock.js rename to 1-js/09-classes/01-class/1-rewrite-to-class/solution.view/clock.js diff --git a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/solution.view/index.html b/1-js/09-classes/01-class/1-rewrite-to-class/solution.view/index.html similarity index 100% rename from 1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/solution.view/index.html rename to 1-js/09-classes/01-class/1-rewrite-to-class/solution.view/index.html diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/clock.js b/1-js/09-classes/01-class/1-rewrite-to-class/source.view/clock.js similarity index 100% rename from 1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/source.view/clock.js rename to 1-js/09-classes/01-class/1-rewrite-to-class/source.view/clock.js diff --git a/1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/index.html b/1-js/09-classes/01-class/1-rewrite-to-class/source.view/index.html similarity index 100% rename from 1-js/07-object-oriented-programming/08-class-patterns/2-rewrite-to-prototypes/source.view/index.html rename to 1-js/09-classes/01-class/1-rewrite-to-class/source.view/index.html diff --git a/1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/task.md b/1-js/09-classes/01-class/1-rewrite-to-class/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/09-class/1-rewrite-to-class/task.md rename to 1-js/09-classes/01-class/1-rewrite-to-class/task.md diff --git a/1-js/09-classes/01-class/animal-rabbit-extends.svg b/1-js/09-classes/01-class/animal-rabbit-extends.svg new file mode 100644 index 0000000000..7a55a5043c --- /dev/null +++ b/1-js/09-classes/01-class/animal-rabbit-extends.svg @@ -0,0 +1,64 @@ + + + + animal-rabbit-extends.svg + Created with sketchtool. + + + + + constructor: Animal + run: function + stop: function + + + + Animal.prototype + + + + constructor: Rabbit + hide: function + + + Rabbit.prototype + + + + Animal + + + + Rabbit + + + new Rabbit + + + + + [[Prototype]] + + + + [[Prototype]] + + + prototype + + + + prototype + + + name: "White Rabbit" + + + constructor + + + constructor + + + + \ No newline at end of file diff --git a/1-js/09-classes/01-class/article.md b/1-js/09-classes/01-class/article.md new file mode 100644 index 0000000000..4980210b93 --- /dev/null +++ b/1-js/09-classes/01-class/article.md @@ -0,0 +1,430 @@ + +# クラス(Class) 基本構文 + +```quote author="Wikipedia" +オブジェクト指向プログラミングでは、*クラス* はオブジェクト生成、状態(メンバ変数)の初期値の提供や振る舞いの実装(メンバ関数またはメソッド)のための拡張可能なプログラムコードテンプレートです。 +``` + +実践では、ユーザや商品など、同じ種類のオブジェクトを大量に作成することがしばしばあります。 + + の章ですでにご存知の通り、`new function` はそれ場合に役立ちます。 + +ですが、最新の JavaScript では、より高度な "class" 構造があり、オブジェクト指向プログラミングに役立つ優れた新機能が導入されています。 + +## "class" 構文 + +基本の構文は次の通りです: +```js +class MyClass { + // クラスメソッド + constructor() { ... } + method1() { ... } + method2() { ... } + method3() { ... } + ... +} +``` + +その後、`new MyClass()` で、リストされたすべてのメソッドをもつ新しいオブジェクトを作成します。 + +`constructor()` メソッドは `new` により自動で呼び出され、そこでオブジェクトを初期化できます。 + +例 + +```js run +class User { + + constructor(name) { + this.name = name; + } + + sayHi() { + alert(this.name); + } + +} + +// 使い方: +let user = new User("John"); +user.sayHi(); +``` + +`new User("John")` が呼び出されると: +1. 新しいオブジェクトが作られます。 +2. 指定された引数で `constructor` が実行され、`this.name` へ代入します。 + +...以降、`user.sayHi()` のように、オブジェクトメソッドが呼び出せます。 + + +```warn header="クラスメソッドの間にはカンマは不要です" +初心者の開発者の落とし穴として、クラスメソッドの間にカンマを置くことがあります。これは構文エラーになります。 + +ここでの表記はオブジェクトリテラルと混同しないでください。クラス内では、カンマは必要ありません。 +``` + +# クラスとは? + +では、`class` は正確に何でしょうか?これはまったく新しい言語レベルのエンティティではありません。 + +魔法を解き明かして、クラスが実際に何であるか見てみましょう。これは多くの複雑な側面を理解するのに役立ちます。 + +JavaScript ではクラスは関数の一種です。 + +これを見てください: + +```js run +class User { + constructor(name) { this.name = name; } + sayHi() { alert(this.name); } +} + +// 証拠: User は function です +*!* +alert(typeof User); // function +*/!* +``` + +`class User {...}` 構造は実際に行っていることは以下です: + +1. クラス宣言の結果となる `User` と言う名前の関数を作成します。関数コードは `constructor` メソッドです(メソッドがない場合は空と想定します)。 +2. `User.prototype` に、`sayHi` などのクラスメソッドを格納します。 + +`new User` オブジェクトが作成された後、そのメソッドを呼び出すと、 の章で説明しように、プロトタイプから取得されます。従って、オブジェクトはクラスメソッドへのアクセスを持ちます。 + +`class User` 宣言の結果を次のように説明できます: + +![](class-user.svg) + +これを確認するコードは以下です: + +```js run +class User { + constructor(name) { this.name = name; } + sayHi() { alert(this.name); } +} + +// class は function +alert(typeof User); // function + +// ...あるいは, より正確には User は constructor メソッド +alert(User === User.prototype.constructor); // true + +// メソッドは User.prototype にあります e.g: +alert(User.prototype.sayHi); // sayHi メソッドのコード + +// prototype には正確には2つのメソッドがあります +alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi +``` + +## 単なるシンタックスシュガーではありません + +`class` は "シンタックスシュガー"(新しいものは導入されていないが、より可読性が高い書き方)という人が時々います。実際、`class` キーワードを使わずに同じものを宣言することが可能です。: + +```js run +// 純粋な関数で User クラスを書き換え + +// 1. constructor 関数を作成 +function User(name) { + this.name = name; +} +// 関数 prototype は "constructor" プロパティをデフォルトで持ちます +// なので、作成は不要です + +// 2. prototype へメソッドを追加 +User.prototype.sayHi = function() { + alert(this.name); +}; + +// 使い方: +let user = new User("John"); +user.sayHi(); +``` + +この定義の結果はほぼ同じです。なので、コンストラクタとそのプロトタイプメソッドを一緒に定義するための、`class` のシンタックスシュガーとみなされる理由はたしかにあります。 + +ですが、重要な違いがあります。 + +1. まず、`class` で生成された関数は特別な内部プロパティ `[[IsClassConstructor]]: true` でラベル付けされています。そのため、手動で作成するのとまったく同じではありません。 + + 言語は様々な箇所でそのプロパティをチェックします。例えば通常の関数とは異なり、`new` で呼び出す必要があります: + + ```js run + class User { + constructor() {} + } + + alert(typeof User); // function + User(); // Error: クラスのコンストラクタ User は `new` なしで呼び出せません + ``` + + また、ほとんどの JavaScript エンジンではクラスのコンストラクタの文字列表現は、"class..." で始まります + + ```js run + class User { + constructor() {} + } + + alert(User); // class User { ... } + ``` + 他の違いもあります。この後見ていきます。 + +2. クラス メソッドは列挙不可です + + クラス定義は、`"prototype"` にあるすべてのメソッドに対して `enumerable` フラグを `false` にセットします。 + + オブジェクトを `for...in` するとき、通常はクラスメソッドは必要ないのでこれは便利です。 + +3. クラスは常に `use strict` です + + クラス構造の中のコードはすべて自動で strict モードです。 + +加えて、`class` 構文には後で説明するような多くの機能があります。 + +## クラス表現 + +関数と同じように、クラスも別の式の中で定義し、渡したり、返却したり代入することができます。 + +これはクラス式の例です。: + +```js +let User = class { + sayHi() { + alert("Hello"); + } +}; +``` + +名前付き関数と同様、クラスも名前を持つことができます。 + +クラス式に名前がある場合、そのクラス内部でのみ見えます: + +```js run +// "名前付きクラス式" +// (スペックにはこのような用語はありませんが、名前付き関数式と同じです) +let User = class *!*MyClass*/!* { + sayHi() { + alert(MyClass); // MyClass の名前はクラスの内部でのみ見えます + } +}; + +new User().sayHi(); // 動作します, MyClass の定義を表示 + +alert(MyClass); // error, MyClass の名前はクラスの外からは見えません +``` + +次のように、クラスを動的に "要求に応じて" 作ることもできます。: + +```js run +function makeClass(phrase) { + // クラス定義とその返却 + return class { + sayHi() { + alert(phrase); + } + }; +} + +// 新しいクラスを作成 +let User = makeClass("Hello"); + +new User().sayHi(); // Hello +``` + + +### Getters/setters + +リテラルオブジェクトのように、クラスも getters/setters, 算出プロパティなどを含めることができます。 + +これは、`get/set` を使用して実装された `user.name` の例です: + +```js run +class User { + + constructor(name) { + // setter を呼び出す + this.name = name; + } + +*!* + get name() { +*/!* + return this._name; + } + +*!* + set name(value) { +*/!* + if (value.length < 4) { + alert("Name too short."); + return; + } + this._name = value; + } + +} + +let user = new User("John"); +alert(user.name); // John + +user = new User(""); // Name too short. +``` + +技術的には、このようなクラス宣言は `User.prototype` に getter / setter を作成することで機能します。 + +## 計算された名前(computed name) + +これは括弧 `[...]` を使用した計算されたメソッド名の例です。 + +```js run +class User { + +*!* + ['say' + 'Hi']() { +*/!* + alert("Hello"); + } + +} + +new User().sayHi(); +``` + +このような特徴は、リテラルオブジェクトに似ているので、覚えやすいと思います。 + +## クラスフィールド + +```warn header="古いブラウザではポリフィルが必要な場合があります" +クラスフィールドは最近言語に追加されたものです。 +``` + +以前は、クラスはメソッドだけを持っていました。 + +"クラスフィールド" は任意のプロパティが追加できる構文です。 + +例えば、`class User` に `name` プロパティを追加しましょう。 + +```js run +class User { +*!* + name = "John"; +*/!* + + sayHi() { + alert(`Hello, ${this.name}!`); + } +} + +new User().sayHi(); // Hello, John! +``` + +つまり、宣言の中で、" = " と記述するだけです。 + +クラスフィールドの重要な違いは、`User.prototype` ではなく、個々のオブジェクトにセットされることです。: + +```js run +class User { +*!* + name = "John"; +*/!* +} + +let user = new User(); +alert(user.name); // John +alert(User.prototype.name); // undefined +``` + +また、より複雑な式や関数呼び出しで値を代入することもできます。: + +```js run +class User { +*!* + name = prompt("Name, please?", "John"); +*/!* +} + +let user = new User(); +alert(user.name); // John +``` + + +### クラスフィールドでバインドされたメソッドを作成する + +章 でデモしたように、JavaScript での関数は動的な `this` を持ちます。これは呼び出しのコンテキストに依存します。 + +そのため、オブジェクトメソッドが渡され、別のコンテキストで呼び出された場合、`this` はもうそのオブジェクトの参照ではありません。 + +例えば、このコードは `undefined` になります: + +```js run +class Button { + constructor(value) { + this.value = value; + } + + click() { + alert(this.value); + } +} + +let button = new Button("hello"); + +*!* +setTimeout(button.click, 1000); // undefined +*/!* +``` + +問題は "`this` なし" で呼び出されたことです。 + +章 で議論したように、これを直す2つのアプローチがあります。: + +1. `setTimeout(() => button.click(), 1000)` のようにラッパー関数を渡す。 +2. メソッドをオブジェクトにバインドする。 e.g. コンストラクタにて。 + +クラスフィールドは別の、すばらしい構文を提供します: + +```js run +class Button { + constructor(value) { + this.value = value; + } +*!* + click = () => { + alert(this.value); + } +*/!* +} + +let button = new Button("hello"); + +setTimeout(button.click, 1000); // hello +``` + +クラスフィールド `click = () => {...}` はオブジェクトごとに作られ、`Button` オブジェクトごとに別々の関数です。そして、`this` はそのオブジェクトを参照します。どこで `button.click` を渡しても、`this` は常に正しい値になります。 + +これはイベントリスナーなど、ブラウザ環境で特に役立ちます。 + +## サマリ + +基本のクラス構文は次のようになります。: + +```js +class MyClass { + prop = value; // プロパティ + + constructor(...) { // コンストラクタ + // ... + } + + method(...) {} // メソッド + + get something(...) {} // getter + set something(...) {} // setter + + [Symbol.iterator]() {} // 計算された名前のメソッド (ここではシンボル) + // ... +} +``` + +`MyClass` は技術的には関数(`constructor` として提供)で、メソッド、getter / setter は `MyClass.prototype` に記述されます。 + +次の章では、継承など他の機能を含め、クラスにてより詳しく学びます。 diff --git a/1-js/09-classes/01-class/class-user.svg b/1-js/09-classes/01-class/class-user.svg new file mode 100644 index 0000000000..418d71d187 --- /dev/null +++ b/1-js/09-classes/01-class/class-user.svg @@ -0,0 +1 @@ +sayHi: functionUserUser.prototypeprototypeconstructor: User \ No newline at end of file diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/1-class-constructor-error/solution.md b/1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/1-class-constructor-error/solution.md rename to 1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/1-class-constructor-error/task.md b/1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/1-class-constructor-error/task.md rename to 1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md diff --git a/3-animation/2-css-animations/3-animate-circle/solution.md b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.md similarity index 100% rename from 3-animation/2-css-animations/3-animate-circle/solution.md rename to 1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.md diff --git a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/clock.js b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/clock.js new file mode 100644 index 0000000000..8009273e3f --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/clock.js @@ -0,0 +1,34 @@ +class Clock { + constructor({ template }) { + this._template = template; + } + + _render() { + let date = new Date(); + + let hours = date.getHours(); + if (hours < 10) hours = '0' + hours; + + let mins = date.getMinutes(); + if (mins < 10) mins = '0' + mins; + + let secs = date.getSeconds(); + if (secs < 10) secs = '0' + secs; + + let output = this._template + .replace('h', hours) + .replace('m', mins) + .replace('s', secs); + + console.log(output); + } + + stop() { + clearInterval(this._timer); + } + + start() { + this._render(); + this._timer = setInterval(() => this._render(), 1000); + } +} diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js rename to 1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/index.html b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/index.html similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/solution.view/index.html rename to 1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/index.html diff --git a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/clock.js b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/clock.js new file mode 100644 index 0000000000..8009273e3f --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/clock.js @@ -0,0 +1,34 @@ +class Clock { + constructor({ template }) { + this._template = template; + } + + _render() { + let date = new Date(); + + let hours = date.getHours(); + if (hours < 10) hours = '0' + hours; + + let mins = date.getMinutes(); + if (mins < 10) mins = '0' + mins; + + let secs = date.getSeconds(); + if (secs < 10) secs = '0' + secs; + + let output = this._template + .replace('h', hours) + .replace('m', mins) + .replace('s', secs); + + console.log(output); + } + + stop() { + clearInterval(this._timer); + } + + start() { + this._render(); + this._timer = setInterval(() => this._render(), 1000); + } +} diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/index.html b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/index.html similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/2-clock-class-extended/source.view/index.html rename to 1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/index.html diff --git a/1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md new file mode 100644 index 0000000000..29be5d86bb --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md @@ -0,0 +1,12 @@ +importance: 5 + +--- + +# 拡張された時計 + +私たちは `Clock` クラスを持っています。今のところ、毎秒時間を表示します。 + +`Clock` を継承した新たなクラス `ExtendedClock` を作成し、`precision` パラメータを追加してください -- "時計のカチカチ" の間の `ms` の数値です。デフォルトでは `1000` (1秒) です。 + +- あなたのコードはファイル `extended-clock.js` にしてください。 +- オジリナルの `clock.js` は変更しないでください。それを拡張してください。 diff --git a/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object.svg b/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object.svg new file mode 100644 index 0000000000..8ab5682913 --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/3-class-extend-object/rabbit-extends-object.svg @@ -0,0 +1,67 @@ + + + + rabbit-extends-object.svg + Created with sketchtool. + + + + + call: function + bind: function + ... + + + + Function.prototype + + + + constructor + + + Object + + + Rabbit + + + + [[Prototype]] + + + + [[Prototype]] + + + constructor + + + + call: function + bind: function + ... + + + + Function.prototype + + + Rabbit + + + + [[Prototype]] + + + constructor + + + class Rabbit + + + class Rabbit extends Object + + + + \ No newline at end of file diff --git a/1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md b/1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md new file mode 100644 index 0000000000..abe36bad8f --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/3-class-extend-object/solution.md @@ -0,0 +1,87 @@ +解答は2つパートがあります。 + +最初に、簡単な方は、継承しているクラスはコンストラクタで `super()` を呼ぶ必要があるということです。そうでなければ `"this"` が "定義済み" になりません。 + +なので、次のように直します: + +```js run +class Rabbit extends Object { + constructor(name) { +*!* + super(); // 継承しているとき、親コンストラクタを呼ぶ必要があります +*/!* + this.name = name; + } +} + +let rabbit = new Rabbit("Rab"); + +alert( rabbit.hasOwnProperty('name') ); // true +``` + +しかし、これですべてではありません。 + +修正した後でさえ、`"class Rabbit extends Object"` 対 `class Rabbit` では依然として重要な違いがあります。 + +知っている通り、 "extends" 構文は2つのプロトタイプを設定します。: + +1. コンストラクタ関数の `"prototype"` 間(メソッド用) +2. コンストラクタ関数自身の間(静的メソッド用) + +我々のケースでは、`class Rabbit extends Object` では次を意味します: + +```js run +class Rabbit extends Object {} + +alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true +alert( Rabbit.__proto__ === Object ); // (2) true +``` + +従って、このように `Rabbit` 経由で `Object` の静的メソッドにアクセスすることができます。: + +```js run +class Rabbit extends Object {} + +*!* +// 通常は Object.getOwnPropertyNames と呼びます +alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // a,b +*/!* +``` + +また、`extends` を使わない場合 `class Rabbit` は2つ目の参照を持ちません。 + +比較してみてください: + +```js run +class Rabbit {} + +alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true +alert( Rabbit.__proto__ === Object ); // (2) false (!) + +*!* +// エラー、Rabbit にこのような関数はありません +alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Error +*/!* +``` + +シンプルな `class Rabbit` では `Rabbit` 関数は同じプロトタイプを持っています。 + +```js run +class Rabbit {} + +// (2) の代わりに、これは正しいです: +alert( Rabbit.__proto__ === Function.prototype ); +``` + +ところで、`Function.prototype` は "一般的な" 関数メソッドを持っています。例えば `call`, `bind` などです。それらは究極的には両方のケースで利用可能です。なぜなら、組み込みの `Object` コンストラクタに対して、`Object.__proto__ === Function.prototype` だからです。 + +これはその図です: + +![](rabbit-extends-object.svg) + +従って、まとめると2つの違いがあります。: + +| class Rabbit | class Rabbit extends Object | +|--------------|------------------------------| +| -- | コンストラクタで `super()` を呼ぶ必要がある | +| `Rabbit.__proto__ === Function.prototype` | `Rabbit.__proto__ === Object` | diff --git a/1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/task.md b/1-js/09-classes/02-class-inheritance/3-class-extend-object/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/10-class-inheritance/3-class-extend-object/task.md rename to 1-js/09-classes/02-class-inheritance/3-class-extend-object/task.md diff --git a/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.svg b/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.svg new file mode 100644 index 0000000000..63b5a18a19 --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/animal-rabbit-extends.svg @@ -0,0 +1 @@ +constructor: Animal run: function stop: functionAnimal.prototypeconstructor: Rabbit hide: functionRabbit.prototypeAnimalRabbitnew Rabbit[[Prototype]][[Prototype]]prototypeprototypename: "White Rabbit"constructorconstructorextends \ No newline at end of file diff --git a/1-js/09-classes/02-class-inheritance/article.md b/1-js/09-classes/02-class-inheritance/article.md new file mode 100644 index 0000000000..2a6a7f0930 --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/article.md @@ -0,0 +1,630 @@ + +# クラスの継承 + +クラスの継承は、あるクラスが別のクラスを拡張するための方法です。 + +つまり、既存の機能の上に、新たな機能を作ることができます。 + +## "extends" キーワード + +クラス `Animal` があるとします: + +```js +class Animal { + constructor(name) { + this.speed = 0; + this.name = name; + } + run(speed) { + this.speed += speed; + alert(`${this.name} runs with speed ${this.speed}.`); + } + stop() { + this.speed = 0; + alert(`${this.name} stopped.`); + } +} + +let animal = new Animal("My animal"); +``` + +これは、`animal` オブジェクトと `Animal` クラスを、グラフィカルに表現したものです。: + +![](rabbit-animal-independent-animal.svg) + +...そしてもう1つの `class Rabbit` を作成します。 + +うさぎ(rabbit)は動物(animal)なので、`Rabbit` クラスは `Animal` がベースとなり、animal メソッドへアクセスできます。これで、"一般的な" 動物が実行できることをうさぎも実行できます。 + +別のクラスを拡張する構文は `class Child extends Parent` です。 + +`Animal` を継承した `class Rabbit` を作成しましょう。: + +```js +*!* +class Rabbit extends Animal { +*/!* + hide() { + alert(`${this.name} hides!`); + } +} + +let rabbit = new Rabbit("White Rabbit"); + +rabbit.run(5); // White Rabbit runs with speed 5. +rabbit.hide(); // White Rabbit hides! +``` + +`Rabbit` クラスのオブジェクトは、`rabbit.hide()` のように `Rabbit` のメソッドと、`rabbit.run()` のように `Animal` メソッド両方が利用できます。 + +内部では、`extends` キーワードは、以前からあるプロトタイプの仕組みを使用して動作しています。`Rabbit.prototype.[[Prototype]]` を `Animal.prototype` にセットします。そのため、`Rabbit.prototype` でメソッドが見つからない場合には、JavaScript は `Animal.prototype` から取得します。 + +![](animal-rabbit-extends.svg) + +例えば、`rabbit.run` メソッドを見つけるために、エンジンは次のようにチェックします(図の下から上): +1. `rabbit` オブジェクト(`run` はありません)。 +2. そのプロトタイプ、つまり `Rabbit.prototype`(`hide` はありますが、`run`はありません)。 +3. そのプロトタイプ、つまり (`extends` しているため) `Animal.prototype`, (最終的に `run` メソッドを持ちます)。 + + の章から想起できるように、JavaScript は同じプロトタイプ継承を組み込みオブジェクトに対しても使います。E.g. `Date.prototype.[[Prototype]]` は `Object.prototype` なので、date は一般的なオブジェクトメソッドを持っています。 + +````smart header="`extends` の後では任意の式が指定できます" +クラス構文では単にクラスではなく、`extends` の後に任意の式を指定することができます。 + +例えば、親クラスを生成する関数呼び出します: + +```js run +function f(phrase) { + return class { + sayHi() { alert(phrase) } + } +} + +*!* +class User extends f("Hello") {} +*/!* + +new User().sayHi(); // Hello +``` +ここでは、 `class User` は `f("Hello")` の結果を継承しています。 + +多くの条件に依存したクラスを生成するために関数を使用し、それを継承するといった高度なプログラミングパターンに対して役立つ場合があります。 +```` + +## メソッドのオーバーライド + +では、前に進めてメソッドをオーバライドをしてみましょう。デフォルトでは、`class Rabbit` では指定されておらず、`class Animal` から直接 "そのまま" 取得しています。 + +ですが、`Rabbit` で自身の `stop` を指定すると、代わりにそれが使われます。: + +```js +class Rabbit extends Animal { + stop() { + // ...class Animal の stop() の代わりに + // rabbit.stop() で利用されます + } +} +``` + +通常は親メソッドを完全に置き換えるのではなく、その上に機能の微調整または拡張を行うことを望みます。メソッド内で何かをしますが、その前後または処理中に親メソッドを呼び出します。 + +クラスはそのために `"super"` キーワードを提供しています。 + +- `super.method(...)` は親メソッドを呼び出します。 +- `super(...)` は親のコンストラクタを呼び出します(コンストラクタの内側でのみ)。 + +例えば、うさぎ(rabbit)が止まったとき自動的に隠れさせます。: + +```js run +class Animal { + + constructor(name) { + this.speed = 0; + this.name = name; + } + + run(speed) { + this.speed += speed; + alert(`${this.name} runs with speed ${this.speed}.`); + } + + stop() { + this.speed = 0; + alert(`${this.name} stopped.`); + } + +} + +class Rabbit extends Animal { + hide() { + alert(`${this.name} hides!`); + } + +*!* + stop() { + super.stop(); // 親の stop 呼び出し + this.hide(); // その後隠す + } +*/!* +} + +let rabbit = new Rabbit("White Rabbit"); + +rabbit.run(5); // White Rabbit runs with speed 5. +rabbit.stop(); // White Rabbit stopped. White rabbit hides! +``` + +今、 `Rabbit` は処理の中で親 `super.stop()` を呼び出す `stop` メソッドを持ちます。 + +````smart header="アロー関数は `super` を持っていません" + の章で述べた通り、アロー関数には `super` がありません。 + +もしアクセスすると、外部の関数から取得されます。例えば: +```js +class Rabbit extends Animal { + stop() { + setTimeout(() => super.stop(), 1000); // 1秒後、親の stop を実行 + } +} +``` + +アロー関数での `super` は、 `stop()` 内での `super` と同じです。なので、意図通りに動きます。以下のように "通常の" 関数を指定するとエラーになります。: + +```js +// Unexpected super +setTimeout(function() { super.stop() }, 1000); +``` +```` + + +## コンストラクタのオーバライド + +コンストラクタに対しては、少し用心が必要です。 + +今まで、`Rabbit` は自身の `constructor` を持っていませんでした。 + +[仕様(specification)](https://tc39.github.io/ecma262/#sec-runtime-semantics-classdefinitionevaluation)によると、クラスが別のクラスを拡張し、`constructor` を持たない場合、次のような `constructor` が生成されます。 + +```js +class Rabbit extends Animal { + // 独自のコンストラクタを持たないクラスを拡張するために生成されます +*!* + constructor(...args) { + super(...args); + } +*/!* +} +``` + +ご覧の通り、基本的にはすべての引数を渡して親の `constructor` を呼び出します。それは自身のコンストラクタを書いていない場合に起こります。 + +では、カスタムのコンストラクタを `Rabbit` に追加してみましょう。それは `name` に加えて `earLength` を指定します。: + +```js run +class Animal { + constructor(name) { + this.speed = 0; + this.name = name; + } + // ... +} + +class Rabbit extends Animal { + +*!* + constructor(name, earLength) { + this.speed = 0; + this.name = name; + this.earLength = earLength; + } +*/!* + + // ... +} + +*!* +// 動作しません! +let rabbit = new Rabbit("White Rabbit", 10); // Error: this は定義されていません +*/!* +``` + +おっと! エラーになりました。これではうさぎを作ることができません。何が間違っていたのでしょう? + +簡単な回答: + +- **継承したクラスのコンストラクタは `super(...)` を呼び出し、(!) `this` を使う前にそれを行わなければなりません。** + +...しかしなぜでしょうか? ここで何が起きているのでしょう? 確かにこの要件は奇妙に見えます。 + +もちろん、これに対する説明がありますので詳細を見てみましょう。これで何が起こっているのかを理解するでしょう。 + +JavaScriptでは、継承しているクラスのコンストラクタ関数(いわゆる "派生コンストラクタ(derived constructor)") とその他の関数で区別があります。派生コンストラクタ関数は特別な内部プロパティ `[[ConstructorKind]]:"derived"` が付けられ、特別な内部のラベルです。 + +このラベルは `new` の振る舞いに影響を与えます、 + +- 通常の関数が `new` で実行される際、空のオブジェクトを作成し、 `this` に割り当てます。 +- ですが、派生コンストラクタが実行されるとき、そうは実行されません。親のコンストラクタがこのジョブを実行することを期待します。 + +なので、親(元になる)コンストラクタを実行するために、派生コンスタクタは `super` の呼び出しが必要になります。そうしないと、`this` のオブジェクトは生成されないからです。結果、エラーになるでしょう。 + +`Rabbit` コンストラクタを動作させるために、`this` を使う前に `super()` を呼びます: + +```js run +class Animal { + + constructor(name) { + this.speed = 0; + this.name = name; + } + + // ... +} + +class Rabbit extends Animal { + + constructor(name, earLength) { +*!* + super(name); +*/!* + this.earLength = earLength; + } + + // ... +} + +*!* +// 今は問題ありません +let rabbit = new Rabbit("White Rabbit", 10); +alert(rabbit.name); // White Rabbit +alert(rabbit.earLength); // 10 +*/!* +``` + + + +### クラスフィールドのオーバーライド: a tricky note + +```warn header="高度な内容です" +ここの内容は、他のプログラミング言語でのクラスに対してある程度経験あること前提としています。 + +言語へのよりよい洞察を提供し、バグの原因になりうる(ただし頻度はそれほど頻繁ではありません)振る舞いについても説明します。 + +理解するのが難しい場合は、先に進んで読み続けてから、しばらくしてから戻ってください。 +``` + +メソッドだけではなく、クラスフィールドもオーバーライドすることができます。 + +ですが、親のコンストラクタでオーバーライドされたフィールドにアクセスする際、多くの他のプログラミング言語とは大きく異なる、トリッキーな振る舞いがあります。 + +この例を考えます: + +```js run +class Animal { + name = 'animal'; + + constructor() { + alert(this.name); // (*) + } +} + +class Rabbit extends Animal { + name = 'rabbit'; +} + +new Animal(); // animal +*!* +new Rabbit(); // animal +*/!* +``` + +ここでは、クラス `Rabbit` は `Animal` を拡張しており、`name` フィールドを自身の値としてオーバーライドしています。 + +`Rabbit` には自身のコンストラクタはないので、`Animal` コンストラクタが呼ばれます。 + +興味深いことは、`new Animal()` と `new Rabbit()` 両方のケースで、行 `(*)` の `alert` は `animal` を表示することです。 + +**言い換えると、親コンストラクタは常にオーバーライドされたものではなく、自身のフィールド値を利用します。** + +何がおかしいでしょうか? + +まだはっきりしない場合は、メソッドの場合と比較してみてください。 + +ここには同じコードがありますが、`this.name` フィールドの代わりに `this.showName()` メソッドを呼んでいます。: + +```js run +class Animal { + showName() { // this.name = 'animal' の代わり + alert('animal'); + } + + constructor() { + this.showName(); // alert(this.name); の代わり + } +} + +class Rabbit extends Animal { + showName() { + alert('rabbit'); + } +} + +new Animal(); // animal +*!* +new Rabbit(); // rabbit +*/!* +``` + +注目してください: 出力結果は異なります。 + +そして、これは自然に期待しているものです。親コンストラクタが派生クラスで呼び出されるとき、オーバーライドされたメソッドが使用されます。 + +...ですが、クラスフィールドの場合はそうではありません。前述のように、親コンストラクタは常に親フィールドを使用します。 + +なぜ差異があるのでしょうか? + +理由は、フィールドの初期化順です。クラスフィールドは次のタイミングで初期化されます: +- ベースクラスのコンストラクタ(何も拡張していない)の前 +- 派生クラスの `super()` の直後 + +今回のケースでは、`Rabbit` は派生クラスで、`constructor()` はありません。以前言った通り、これは`super(...args)` だけがある空のコンストラクタと同じです。 + +そのため、`new Rabbit()` は `super()` を呼び出し、親コンストラクタを実行し、(派生クラスのルールに従い)その後クラスフィールドが初期化されます。親コンストラクタが実行された時点では、まだ `Rabbit` クラスフィールドはないため、`Animal` フィールドが使用されます。 + +フィールドとメソッドのこの微妙な違いは、JavaScript 固有のものです。 + +幸い、この振る舞いはオーバーライドされたフィールドが親コンストラクタの中で使用されている場合にのみです。出くわすと何が起こっているのか理解するのが難しいかもしれないので、ここで説明しています。 + +問題になった場合には、フィールドの代わりにメソッドあるいは getter/setter を使用して修正できます。 + + +## Super: internals, [[HomeObject]] + +```warn header="高度な情報です" +初めてチュートリアルを読んでいるなら、このセクションはスキップして構いません。 + +ここは、継承と `super` の内部の仕組みについて説明しています。 +``` + +`super` の内部をもう少し詳細に見ていきましょう。ここまでで、いくつか興味深いことがあります。 + +まず最初に、今まで学んできたすべてのことだけでは、 `super` が完全動作するのは不可能です! + +確かに、それが技術的にどのように動くのか、自問してみましょう。オブジェクトメソッドを実行するとき、`this` は現在のオブジェクトを取ります。もし `super.method()` を呼び出す場合、エンジンは現在のオブジェクトのプロトタイプから `method` を取得する必要があります。 + +課題はシンプルに見えますが、そうではありません。エンジンは現在のオブジェクト `this` を知っているので、`this.__proto__.method` で親の `method` が取得できるかもしれません。残念ながら、このような "単純な" 解決策は機能しません。 + +問題をデモしてみましょう。簡単にするために、クラスなしで単純なオブジェクトを使用します。 + +詳細を知る必要がなければ、このパートをスキップして `[[HomeObject]]` サブセクションに進んでください。特に問題はありません。ここは物事を深く理解することに興味がある場合に呼んでください。 + +以下の例で、`rabbit.__proto__ = animal` です。`rabbit.eat()` で、`this.__proto__` を使用して、`animal.eat()` メソッドを呼び出します: + +```js run +let animal = { + name: "Animal", + eat() { + alert(this.name + " eats."); + } +}; + +let rabbit = { + __proto__: animal, + name: "Rabbit", + eat() { +*!* + // これがおそらく super.eat() が動作する方法です + this.__proto__.eat.call(this); // (*) +*/!* + } +}; + +rabbit.eat(); // Rabbit eats. +``` + +行 `(*)` でプロトタイプ(`animal`) から `eat` を取り、現在のオブジェクトコンテキストでそれを呼び出します。`.call(this)` はここでは重要であることに注意してください。なぜなら、シンプルな `this.__proto__.eat()` は現在のオブジェクトではなくプロトタイプのコンテキストで親の `eat` を実行するためです。 + +また、上のコードは実際に期待通り動作します: 正しい `alert` になります。 + +今度はもう1つのオブジェクトをチェーンに追加しましょう。 どのように壊れるかを見てみます: + +```js run +let animal = { + name: "Animal", + eat() { + alert(this.name + " eats."); + } +}; + +let rabbit = { + __proto__: animal, + eat() { + // ...bounce around rabbit-style 親 (animal) メソッドを呼び出す + this.__proto__.eat.call(this); // (*) + } +}; + +let longEar = { + __proto__: rabbit, + eat() { + // ...do something with long ears 親 (rabbit) メソッドを呼び出す + this.__proto__.eat.call(this); // (**) + } +}; + +*!* +longEar.eat(); // Error: 最大呼び出しスタックサイズを超えました +*/!* +``` + +コードはこれ以上動作しません! `longEar.ear()` を呼び出そうとするとエラーになります。 + +これは明白ではないかもしれませんが、もし `longEar.eat()` 呼び出しをトレースをするとなぜかが分かります。行 `(*)` と `(**)` は共に、`this` の値は現在のオブジェクト (`longEar`) です。それが肝心です: すべてのオブジェクトメソッドはプロトタイプなどではなく、現在のオブジェクトを `this` として取得します。 + +したがって、行 `(*)` と `(**)` は共に、`this.__proto__` の値は全く同じで、`rabbit` です。それらは両方とも、無限ループで `rabbit.eat` を呼んでいます。 + +これは何が起きているかを示す図です。: + +![](this-super-loop.svg) + +1. `longEar.eat()` の中で、行 `(**)` は `this=longEar` となる `rabbit.eat` を呼び出します。 + ```js + // longEar.eat() の中では this = longEar です + this.__proto__.eat.call(this) // (**) + // なので次のようになります + longEar.__proto__.eat.call(this) + // つまり呼ばれるのは + rabbit.eat.call(this); + ``` +2. 次に `rabbit.eat` の 行 `(*)` で、チェーンの中でより高次へ呼び出しを渡したいですが、`this=longEar` なので、 `this=__prto__.eat` は再び `rabbit.eat` です! + + ```js + // rabbit.eat() の中でも this = longEar です + this.__proto__.eat.call(this) // (*) + // なので次のようになります + longEar.__proto__.eat.call(this) + // なので (再び) + rabbit.eat.call(this); + ``` + +3. ...したがって、それ以上高次へ登ることができないので、`rabbit.eat` はエンドレスで自身を呼び出します。 + +`this` だけを使ってこの問題を解くことはできません。 + +### `[[HomeObject]]` + +その解決策を提供するため、JavaScriptはもう1つ関数のための特別な内部プロパティを追加しています: `[[HomeObject]]` です。 + +関数がクラスまたはオブジェクトメソッドとして指定されたとき、その `[[HomeObject]]` プロパティはそのオブジェクトになります。 + +そして、`super` はこれを使って親のプロトタイプとメソッドメソッドを解決しましま。 + +最初のオブジェクトで、それがどのように動くのか見てみましょう: + +```js run +let animal = { + name: "Animal", + eat() { // [[HomeObject]] == animal + alert(this.name + " eats."); + } +}; + +let rabbit = { + __proto__: animal, + name: "Rabbit", + eat() { // [[HomeObject]] == rabbit + super.eat(); + } +}; + +let longEar = { + __proto__: rabbit, + name: "Long Ear", + eat() { // [[HomeObject]] == longEar + super.eat(); + } +}; + +*!* +// ただしく機能します +longEar.eat(); // Long Ear eats. +*/!* +``` + +`[[HomeObject]]` のより、意図したとおりに動作します。`longEar.eat` のようなメソッドは、その `[[HomeObject]]` を知っており、そのプロトタイプから親のメソッドを取得します。`this` を使用することなく。 + +### メソッドは "自由" ではありません + +これまでの認識の通り、一般的な関数は "自由" であり、JavaScript のオブジェクトにバインドされていません。そのため、オブジェクト間でコピーしたり別の `this` で呼び出すことができます。 + +しかし、メソッドはそのオブジェクトを記録しているため、`[[HomeObject]]` の存在はその原則に違反します。`[[HomeObject]]` は変更できないため、このバインドは永遠です。 + +`[[HomeObject]]` が使用される言語での唯一の場所が `super` です。したがって、メソッドが `super` を使用しない場合、メソッドは以前として自由とみなせ、オブジェクト間でコピーできます。しかし、`super` があると、うまくいかない可能性があります。 + +これはコピー後の上手く行かない `super` の結果のデモです: + +```js run +let animal = { + sayHi() { + alert(`I'm an animal`); + } +}; + +// rabbit は animal を継承 +let rabbit = { + __proto__: animal, + sayHi() { + super.sayHi(); + } +}; + +let plant = { + sayHi() { + alert("I'm a plant"); + } +}; + +// tree は plant を継承 +let tree = { + __proto__: plant, +*!* + sayHi: rabbit.sayHi // (*) +*/!* +}; + +*!* +tree.sayHi(); // I'm an animal (?!?) +*/!* +``` + +`tree.sayHi()` の呼び出しは、"I'm an animal" を表示します。これは間違いです。 + +理由は簡単です: +- 行 `(*)` で、メソッド `tree.sayHi` は `rabbit` からコピーされました。 +- `rabbit` で作られているので、`[[HomeObject]]` は `rabbit` です。`[[HomeObject]]` を変える方法はありません。 +- `tree.sayHi()` のコードは `super.sayHi()` を内部に持ちます。`rabbit` から進み、`animal` よりメソッドを取得します。 + +これが、起きていることの図です。: + +![](super-homeobject-wrong.svg) + +### 関数プロパティではないメソッド + +`[[HomeObject]]` はクラスと単純なオブジェクト両方で定義されたメソッドに対して定義されています。しかし、オブジェクトの場合、メソッドは指定された方法で正確に指定されなければなりません。: `"method: function()"` ではなく `method()` として指定する必要があります。 + +我々にとってこの違いは本質的ではないかもしれませんが、JavaScript にとっては重要です。 + +下の例では、上の例との比較のために非メソッド構文を使っています。`[[HomeObject]]` プロパティはセットされず、継承は動作しません。: + +```js run +let animal = { + eat: function() { // 短縮構文: eat() {...} にする必要があります + // ... + } +}; + +let rabbit = { + __proto__: animal, + eat: function() { + super.eat(); + } +}; + +*!* +rabbit.eat(); // super 呼び出しエラー([[HomeObject]] が無いため) +*/!* +``` + +## サマリ + +1. クラスを拡張するには: `class Child extends Parent`: + - これは `Child.prototype.__proto__` は `Parent.prototype` になることを意味し、メソッドが継承されます。 +2. コンストラクタをオーバーライドするとき: + - `this` を使う前に `Child` コンストラクタの中で `super` として親のコンストラクタを呼ぶ必要があります。 +3. 他のメソッドをオーバーライドするとき: + - `super.method()` を使用することで、`Child` で親のメソッドを呼び出すことができます。 +4. 内部: + - メソッドは内部の `[[HomeObject]]` プロパティで自身のクラス/オブジェクトを覚えています。 これが `super` が親メソッドを解決する方法です。 + - そのため、あるオブジェクトから別のオブジェクトへ `super` を持つメソッドをコピーするのは安全ではありません。 + +また: + - アロー関数は独自の `this` や `super` を持っていないので、周囲の文脈に透過的にフィットします。 diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-array-object.svg b/1-js/09-classes/02-class-inheritance/class-inheritance-array-object.svg new file mode 100644 index 0000000000..5ea9bf29ea --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/class-inheritance-array-object.svg @@ -0,0 +1 @@ +slice: function ...Array.prototypearrhasOwnProperty: function ...Object.prototype[1, 2, 3][[Prototype]][[Prototype]] \ No newline at end of file diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2.svg b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2.svg new file mode 100644 index 0000000000..72e47e34c2 --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal-2.svg @@ -0,0 +1 @@ +jump: functionRabbit.prototyperabbiteat: functionAnimal.prototypename: "White Rabbit"[[Prototype]][[Prototype]]Rabbit.prototype.__proto__ = Animal.prototype sets thistoString: function hasOwnProperty: function ...Object.prototype[[Prototype]][[Prototype]]null \ No newline at end of file diff --git a/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal.svg b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal.svg new file mode 100644 index 0000000000..bced3d355e --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/class-inheritance-rabbit-animal.svg @@ -0,0 +1 @@ +methods of RabbitRabbit.prototyperabbitmethods of AnimalAnimal.prototype[[Prototype]][[Prototype]]properties of rabbit \ No newline at end of file diff --git a/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal.svg b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal.svg new file mode 100644 index 0000000000..f53fc92dee --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-animal.svg @@ -0,0 +1 @@ + constructor: Animal run: function stop: functionAnimal.prototypeAnimalnew Animal[[Prototype]]prototypename: "My animal" \ No newline at end of file diff --git a/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit.svg b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit.svg new file mode 100644 index 0000000000..2f30a3a901 --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/rabbit-animal-independent-rabbit.svg @@ -0,0 +1 @@ + constructor: Rabbit hide: functionRabbit.prototypeRabbitnew Rabbit[[Prototype]]prototypename: "My rabbit" \ No newline at end of file diff --git a/1-js/09-classes/02-class-inheritance/super-homeobject-wrong.svg b/1-js/09-classes/02-class-inheritance/super-homeobject-wrong.svg new file mode 100644 index 0000000000..f6450ddc49 --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/super-homeobject-wrong.svg @@ -0,0 +1 @@ +sayHiplantsayHitreesayHianimalrabbit[[HomeObject]]sayHi \ No newline at end of file diff --git a/1-js/09-classes/02-class-inheritance/this-super-loop.svg b/1-js/09-classes/02-class-inheritance/this-super-loop.svg new file mode 100644 index 0000000000..4f5f45034a --- /dev/null +++ b/1-js/09-classes/02-class-inheritance/this-super-loop.svg @@ -0,0 +1 @@ +rabbitlongEarrabbitlongEar \ No newline at end of file diff --git a/1-js/09-classes/03-static-properties-methods/animal-rabbit-static.svg b/1-js/09-classes/03-static-properties-methods/animal-rabbit-static.svg new file mode 100644 index 0000000000..3e354b895d --- /dev/null +++ b/1-js/09-classes/03-static-properties-methods/animal-rabbit-static.svg @@ -0,0 +1 @@ +constructor: Animal run: functionAnimal.prototypeconstructor: Rabbit hide: functionRabbit.prototypeAnimalRabbitrabbit[[Prototype]][[Prototype]][[Prototype]]prototypeprototypecomparename: "White Rabbit" \ No newline at end of file diff --git a/1-js/09-classes/03-static-properties-methods/article.md b/1-js/09-classes/03-static-properties-methods/article.md new file mode 100644 index 0000000000..caef4925f6 --- /dev/null +++ b/1-js/09-classes/03-static-properties-methods/article.md @@ -0,0 +1,237 @@ +# 静的(static)プロパティとメソッド + +クラス全体にメソッドを割り当てることもできます。このようなメソッドは _static(静的)_ と呼ばれます。 + +クラス宣言の中では、次のように `static` キーワードを付けます。 + +```js run +class User { +*!* + static staticMethod() { +*/!* + alert(this === User); + } +} + +User.staticMethod(); // true +``` + +これは、実際にはプロパティとして直接割り当てるのと同じことをします。: + +```js +class User {} + +User.staticMethod = function () { + alert(this === User); +}; + +User.staticMethod(); // true +``` + +`User.staticMethod()` 内の `this` の値はクラスコンストラクタ `User` 自身("ドットの前のオブジェクト" ルール)です。 + +通常 static メソッドは、クラスには属するが、特定のオブジェクトには属さない関数を実装するのに使用されます。 + +例えば、`Article` オブジェクトがあり、それらを比較するための関数が必要とします。 + +自然な解決策は、静的メソッド `Article.compare` を追加することです。: + +```js run +class Article { + constructor(title, date) { + this.title = title; + this.date = date; + } + +*!* + static compare(articleA, articleB) { + return articleA.date - articleB.date; + } +*/!* +} + +// usage +let articles = [ + new Article("Mind", new Date(2019, 1, 1)), + new Article("Body", new Date(2019, 0, 1)), + new Article("JavaScript", new Date(2019, 11, 1)) +]; + +*!* +articles.sort(Article.compare); +*/!* + +alert( articles[0].title ); // Body +``` + +ここでは、`Article.compare` は記事を比較する手段として、記事の “上位” にいます。記事のメソッドと言うよりはクラス全体のメソッドです。 + +別の例は、いわゆる “ファクトリー” メソッドです。 + +記事の作成には複数の方法が必要です: + +1. 与えられたパラメータ(`title`, `date` など)による作成 +2. 今日の日付の空の記事の作成 +3. ... + +最初の方法はコンストラクタで実装することができます。そして、2 つ目の方法としてはクラスの静的メソッドを作ることができます。 + +ここでの `Article.createTodays()` を見てください: + +```js run +class Article { + constructor(title, date) { + this.title = title; + this.date = date; + } + +*!* + static createTodays() { + // 思い出してください, this = Article + return new this("Today's digest", new Date()); + } +*/!* +} + +let article = Article.createTodays(); + +alert( article.title ); // Todays digest +``` + +これで今日のダイジェストを作成する必要があるたびに、`Article.createTodays()` を呼べます。改めて言いますが、これは記事のメソッドではなく、クラス全体のメソッドです。 + +静的メソッドは、次のように、データベース関連のクラスでデータベースの検索/保存/削除のためにも使用されます。: + +```js +// Article は記事を管理するための特別なクラスと仮定します +// 記事を削除するための static メソッド: +Article.remove({ id: 12345 }); +``` + +## 静的プロパティ + +[recent browser=Chrome] + +静的プロパティも可能で、通常のクラスプロパティと同じように見えますが、先頭に `static` が付きます。 + +```js run +class Article { + static publisher = "Ilya Kantor"; +} + +alert(Article.publisher); // Ilya Kantor +``` + +これは直接 `Article` に代入するのと同じです。: + +```js +Article.publisher = "Ilya Kantor"; +``` + +## 静的プロパティとメソッドの継承 [#statics-and-inheritance] + +静的プロパティとメソッドは継承されます。 + +例えば、以下のコードの `Animal.compare` と `Animal.planet` は継承され、`Rabbit.compare` と `Rabbit.planet` としてアクセス可能です。 + +```js run +class Animal { + static planet = "Earth"; + + constructor(name, speed) { + this.speed = speed; + this.name = name; + } + + run(speed = 0) { + this.speed += speed; + alert(`${this.name} runs with speed ${this.speed}.`); + } + +*!* + static compare(animalA, animalB) { + return animalA.speed - animalB.speed; + } +*/!* + +} + +// Inherit from Animal +class Rabbit extends Animal { + hide() { + alert(`${this.name} hides!`); + } +} + +let rabbits = [ + new Rabbit("White Rabbit", 10), + new Rabbit("Black Rabbit", 5) +]; + +*!* +rabbits.sort(Rabbit.compare); +*/!* + +rabbits[0].run(); // Black Rabbit runs with speed 5. + +alert(Rabbit.planet); // Earth +``` + +`Rabbit.compare` を呼び出すと、継承された `Animal.compare` が呼び出されます。 + +これはどのように機能しているでしょう?すでに推測したかもしれませんが、`extends` もまた `Rabbit` に `Animal` への参照を持つ `[[Prototype]]` を与えます。 + +![](animal-rabbit-static.svg) + +したがって, `Rabbit extends Animal` は 2 つの `[[Prototype]]` の参照を作成します: + +1. `Rabbit` 関数は プロトタイプ的に `Animal` 関数を継承しています。 +2. `Rabbit.prototype` はプロトタイプ的に `Animal.prototype` を継承しています。 + +結果、継承は通常のものと静的なメソッド両方で機能します。 + +ここで、それを確認しましょう: + +```js run +class Animal {} +class Rabbit extends Animal {} + +// 静的 +alert(Rabbit.__proto__ === Animal); // true + +// 通常のメソッド +alert(Rabbit.prototype.__proto__ === Animal.prototype); // true +``` + +## サマリ + +静的メソッドは、具体的なクラスインスタンスに関連する機能やインスタンスが存在することを必要する機能ではなく、むしろクラス全体に属している機能のために使用されます。 + +例えば、比較用のメソッド `Article.compare(article1, article2)` や、ファクトリーメソッド `Article.createTodays()` です。 + +これらはクラス宣言の中で `static` と言うキーワードでラベル付けされます。 + +静的プロパティは、クラスレベルのデータを格納するときに使用され、インスタンスにバインドされません。 + +構文は次の通りです: + +```js +class MyClass { + static property = ...; + + static method() { + ... + } +} +``` + +これは技術的には、クラス自身への代入と同じです: + +```js +MyClass.property = ... +MyClass.method = ... +``` + +静的プロパティは継承されます。 + +`class B extends A` の場合、クラス `B` 自体のプロトタイプは `A` を指します、: `B.[[Prototype]] = A`。したがって、`B` の中にフィールドが見つからない場合は、検索は `A` の中で続行されます。 diff --git a/1-js/09-classes/04-private-protected-properties-methods/article.md b/1-js/09-classes/04-private-protected-properties-methods/article.md new file mode 100644 index 0000000000..0653aefa60 --- /dev/null +++ b/1-js/09-classes/04-private-protected-properties-methods/article.md @@ -0,0 +1,317 @@ +# Private / protected プロパティとメソッド + +オブジェクト指向プログラミングの最も重要な原則の 1 つは、内部インタフェースを外部インタフェースから切り離すことです。 + +これは、"hello world" アプリケーションよりも複雑なものを作るすべての開発において "必須" です。 + +これを理解するために、開発から離れて、現実の世界に目を向けましょう。 + +通常、私たちが使っているデバイスは非常に複雑です。しかし、内部インタフェースを外部インタフェースから切り離すことで、問題なく使うことができます。 + +## 実世界の例 + +例えばコーヒーメーカーです。外側はシンプルです: ボタン、ディスプレイやいくつかの穴があるだけです。そして、もちろん結果はおいしいコーヒーです :) + +![](coffee.jpg) + +しかし内側は...(修理マニュアルにある図です) + +![](coffee-inside.jpg) + +多くの構成要素があります。しかし、何も知らなくても私たちは使うことができます。 + +コーヒーメーカーはとても信頼性が高いですね。何年も使え、調子が悪い場合にだけ修理に持っていきます。 + +コーヒーメーカーの信頼性とシンプルさの秘密は、すべての構成要素がよく調整され、内部に _隠れている_ ことです。 + +もしコーヒーメーカーの保護カバーを外すと、使うのが非常に複雑になり(どこを押せばよい?)、危険です(感電するかもしれません)。 + +これから見ていきますが、プログラミングにおいてオブジェクトはコーヒーメーカーのようなものです。 + +しかし、内部の詳細を隠すには、保護カバーではなく言語や規則の特別な構文を使っていきます。 + +## 内部 / 外部インタフェース + +オブジェクト指向プログラミングでは、プロパティとメソッドは 2 つのグループに分けられます: + +- _内部インタフェース_: クラスの他のメソッドからアクセス可能だが、外側からはアクセスできないメソッドやプロパティ。 +- _外部インタフェース_: 外部のクラスからもアクセス可能なメソッドやプロパティ。 + +コーヒーメーカーで例えるなら、内部に隠されているもの: ボイラーチューブや発熱体など、は内部インタフェースです。 + +内部インタフェースはオブジェクトが機能するために使われ、その構成要素はお互いに使用されます。例えば、ボイラーチューブは発熱体に取り付けられます。 + +しかし、コーヒーメーカーの外側からは、誰もそこに届かないよう保護カバーで閉ざされています。内部の詳細は隠されており、アクセスできません。私たちは、外部インタフェースを介してのみその機能を利用できます。 + +したがって、オブジェクトを使用するのに必要なことは、その外部インタフェースを知ることです。内部でどのように動いているか完全に分からないかもしれませんが、問題ありません。 + +ここまでは一般的な前置きでした。 + +JavaScript には、3 種類のプロパティとメンバがあります。 + +- パブリック(public): どこからでもアクセス可能です。これらは外部インタフェースになります。今まで、私たちはパブリックなプロパティとメソッドのみを使用していました。 +- プライベート(private): クラス内部からのみアクセスできます。これらは内部インタフェース用です。 + +他の多くの言語には、"プロテクト(protected)" フィールドも存在します。: これは、クラス及び、そのクラスを継承したサブクラスの内部からのみアクセス可能であることを意味します。これも内部インタフェースには役立ちます。通常は、継承しているクラスを適切に拡張できるよう、それらにアクセスさせたいため、ある意味ではプライベートよりも広く知られています。 + +protected フィールドは言語レベルでは JavaScript に実装されていません。が、実際には非常に便利であるため、エミュレートされています。 + +次のステップでは、これらすべての種類のプロパティを使用した JavaScript でコーヒーメーカーを作ります。コーヒーメーカーには多くの構成要素がありますが、シンプルさを保つためにモデル化はしません(モデル化することも可能です)。 + +## "waterAmount" を保護(protect)する + +最初に、単純なコーヒーメーカークラスを作りましょう。: + +```js run +class CoffeeMachine { + waterAmount = 0; // 内部の水の量 + + constructor(power) { + this.power = power; + alert(`Created a coffee-machine, power: ${power}`); + } +} + +// コーヒーメーカーを生成 +let coffeeMachine = new CoffeeMachine(100); + +// 水を追加 +coffeeMachine.waterAmount = 200; +``` + +今のところ、プロパティ `waterAmount` と `power` は public です。外側から簡単に値を取得したり、任意の値に設定できます。 + +より細かく制御できるように、`waterAmount` プロパティを protected に変更しましょう。例えば、誰もゼロより小さくは設定できないようにしたいです。 + +**protected プロパティは、通常アンダースコア `_` で始まります。** + +これは言語レベルでは強制されていませんが、このようなプロパティやメソッドは外側からアクセスするべきではない、という慣習があります。ほとんどのプログラマはそれに従っています。 + +なので、プロパティは `_waterAmount` になります: + +```js run +class CoffeeMachine { + _waterAmount = 0; + + set waterAmount(value) { + if (value < 0) { + value = 0; + } + this._waterAmount = value; + } + + get waterAmount() { + return this._waterAmount; + } + + constructor(power) { + this._power = power; + } +} + +// コーヒーメーカーを生成 +let coffeeMachine = new CoffeeMachine(100); + +// 水を追加 +coffeeMachine.waterAmount = -10; // _waterAmount は -10 ではなく、 0 になります +``` + +これでアクセスが制御されたので、ゼロより小さい値へ設定は不可能です。 + +## 読み取り専用(Read-only)の "power" + +`power` プロパティは、読み取り専用にしましょう。作成時にのみ設定し、それ以降変更しないプロパティも時にあります。 + +これはまさにコーヒーメーカーの電力(power)のケースです。この値は決して変わりません。 + +そうするためには、getter のみを作成する必要があります。setter は不要です。: + +```js run +class CoffeeMachine { + // ... + + constructor(power) { + this._power = power; + } + + get power() { + return this._power; + } +} + +// コーヒーメーカーを作成 +let coffeeMachine = new CoffeeMachine(100); + +alert(`Power is: ${coffeeMachine.power}W`); // Power is: 100W + +coffeeMachine.power = 25; // Error (setter はないので) +``` + +````smart header="Getter/setter 関数" +ここでは、getter/setter 構文を使いました。 + +しかし、多くの場合は次のような `get.../set...` 関数が好まれます。: + +```js +class CoffeeMachine { + _waterAmount = 0; + + *!*setWaterAmount(value)*/!* { + if (value < 0) throw new Error("Negative water"); + this._waterAmount = value; + } + + *!*getWaterAmount()*/!* { + return this.waterAmount; + } +} + +new CoffeeMachine().setWaterAmount(100); +``` + +これは少し長く見えますが、関数はより柔軟です。たとえ現時点では必要ないとしても、この方法の場合、複数の引数を受け取ることができます。そのため、将来なにかをリファクタする必要がある場合に備えるなら、関数はより安全な選択肢です。 + +もちろん、これはトレードオフです。一方で get/set 構文はより短くかけます。ここに厳密なルールはないので、決めるのはあなた次第です。 +```` + +```smart header="Protected フィールドは継承されます" +`class MegaMachine extends CoffeeMachine` と継承した場合、新しいクラスのメソッドから `this._waterAmount` や `this._power` にアクセスするのを妨げるものは何もありません。 + +つまり、protected フィールは当然のことながら継承可能です。下で見ていく private なものとは異なります。 +``` + +## Private "#waterLimit" + +[recent browser=none] + +プライベートなプロパティやメソッドに対する言語レベルのサポートを提供する、ほぼ標準的な完成した JavaScript の提案があります。 + +プライベートは `#` から始める必要があります。それらはクラス内部からのみアクセス可能です。 + +例えば、ここではプライベートな `#waterLimit` プロパティを追加し、水量をチェックするロジックを別のメソッドに抜き出しています: + +```js run +class CoffeeMachine { +*!* + #waterLimit = 200; +*/!* + +*!* + #fixWaterAmount(value) { + if (value < 0) return 0; + if (value > this.#waterLimit) return this.#waterLimit; + } +*/!* + + setWaterAmount(value) { + this.#waterLimit = this.#fixWaterAmount(value); + } + +} + +let coffeeMachine = new CoffeeMachine(); + +*!* +// can't access privates from outside of the class +coffeeMachine.#fixWaterAmount(123); // Error +coffeeMachine.#waterLimit = 1000; // Error +*/!* +``` + +言語レベルで、`#` はフィールドがプライベートであることを示す特別な記号です。その外側や継承したクラスからアクセスすることはできません。 + +プライベートフィールドはパブリックなものと衝突しません。プライベートな `#waterAmount` とパブリックな `waterAmount` フィールド両方を同時にもつことができます。 + +例えば、`#waterAmount` のアクセサとなる `waterAmount` を作りましょう。: + +```js run +class CoffeeMachine { + #waterAmount = 0; + + get waterAmount() { + return this.#waterAmount; + } + + set waterAmount(value) { + if (value < 0) value = 0; + this.#waterAmount = value; + } +} + +let machine = new CoffeeMachine(); + +machine.waterAmount = 100; +alert(machine.#waterAmount); // Error +``` + +protected なものとは異なり、private フィールドは言語レベルで強制されます。 + +なお、`CoffeeMachine` を継承した場合、`#waterAmount` へアクセスはできません。アクセスするには、`waterAmount` の getter/setter を経由する必要があります。: + +```js +class CoffeeMachine extends CoffeeMachine() { + method() { +*!* + alert( this.#waterAmount ); // Error: CoffeeMachine からのみアクセス可能 +*/!* + } +} +``` + +多くのシナリオにおいて、このような制限は厳しすぎます。`CoffeeMachine` を拡張する際には、その内部にアクセスすべき正当な理由があるかもしれません。そのため、protected フィールドは言語レベルの構文ではサポートされていませんが、多くの場合 protected フィールドが使われています。 + +````warn +Private フィールドは特別です。 + +通常だと this[name] でフィールドにアクセスできます。: + +```js +class User { + ... + sayHi() { + let fieldName = "name"; + alert(`Hello, ${this[fieldName]}`); + } +} +``` + +しかし、private フィールドだとそれはできません。: `this['#name']` は期待通り動作しません。これは private であることを維持するための、構文上の制限になります。 +```` + +## サマリ + +OOP の用語では、外部インタフェースと内部インタフェースを切り離すことを、[カプセル化](<"https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)">) と呼びます。 + +これには次のような利点があります: + +ユーザが自ら墓穴を掘らないようにするための保護 +: 想像してください、コーヒーメーカーを使用している開発者のチームがあるとしましょう。それは "最も優れたコーヒーメーカー" の会社で作られたもので、上手く動作します。が、ある時保護カバーが外され、内部インタフェースが公開された状態になっています。 + + 開発者はみな、意図したとおりにコーヒーメーカーを使用していますが、その中の一人である John がコーヒーメーカーの内部にいくらかの調整を行いました。それにより、2日後、コーヒーメーカーは失敗するようになりました。 + + これはもちろん John のせいではなく、むしろ保護カバーを外し、John に操作をさせた人の責任です。 + + プログラミングでも同じです。外部からの変更を意図していないものが勝手に変更された場合、結果は予測不可能です。 + +Supportable +: プログラミングにおける状況は、実際のコーヒーメーカーでの場合よりも複雑です。なぜなら一度購入するだけではないからです。コードは絶えず開発と改良されます。 + + **もし内部インタフェースを厳密に区切ると、クラスの開発者は利用者へ通知しなくても内部のプロパティとメソッドを自由に変更することができます。** + + 特定のメソッド名を変更したり、パラメータを変更したり、あるいは削除したりすることができることが明らか(外部のコードはそれらに依存していないため)だと、開発は遥かに容易になります。 + + 利用者は、新しいバージョンが登場した際、全体的な見直しになる可能性はありますが、それでも外部インタフェースが同じであればアップグレードは簡単です。 + +複雑さを隠す +: 人々はシンプルなものを使うのを好みます。中身はそうでないかもしれませんが、少なくとも外から見たときは。 + + プログラマも例外ではありません。 + + **実装の詳細が隠されていて、シンプルかつ良くドキュメント化された外部インタフェースが利用可能であることは常に便利です。** + +内部インタフェースを隠すために、proctected または public プロパティを使用します。] + +- protected フィールドは `_` で始まります。これはよく知られた慣習であり、言語レベルで強制されているものではありません。プログラマはそのクラスと、それを継承したクラスからのみ `_` で始まるフィールドにアクセスするべきです。 +- private フィールドは `#` で始まります。JavaScript では、クラス内からのみアクセスできます。 + +現時点では、private フィールドはブラウザ間では十分にはサポートされていませんが、polyfill することができます。 diff --git a/1-js/09-classes/04-private-protected-properties-methods/coffee-inside.jpg b/1-js/09-classes/04-private-protected-properties-methods/coffee-inside.jpg new file mode 100644 index 0000000000..60f84664d1 Binary files /dev/null and b/1-js/09-classes/04-private-protected-properties-methods/coffee-inside.jpg differ diff --git a/1-js/09-classes/04-private-protected-properties-methods/coffee.jpg b/1-js/09-classes/04-private-protected-properties-methods/coffee.jpg new file mode 100644 index 0000000000..ee26e1c064 Binary files /dev/null and b/1-js/09-classes/04-private-protected-properties-methods/coffee.jpg differ diff --git a/1-js/09-classes/05-extend-natives/article.md b/1-js/09-classes/05-extend-natives/article.md new file mode 100644 index 0000000000..8eaafdff5c --- /dev/null +++ b/1-js/09-classes/05-extend-natives/article.md @@ -0,0 +1,89 @@ +# 組み込みのクラスを拡張する + +Array、Map などの組み込みのクラスも拡張可能です。 + +例えば、ここでは `PowerArray` はネイティブの `Array` を継承しています: + +```js run +// 1つメソッドを追加しています(その他のことももちろん可能です) +class PowerArray extends Array { + isEmpty() { + return this.length === 0; + } +} + +let arr = new PowerArray(1, 2, 5, 10, 50); +alert(arr.isEmpty()); // false + +let filteredArr = arr.filter((item) => item >= 10); +alert(filteredArr); // 10, 50 +alert(filteredArr.isEmpty()); // false +``` + +とても興味深い点に注目してください。`filter` や `map` といった組み込みのメソッドは、継承された型 `PowerArray` と同じ型の新しいオブジェクトを返します。そのために、内部の実装はオブジェクトの `constructor` プロパティを使用します。 + +上の例では、 + +```js +arr.constructor === PowerArray; +``` + +`arr.filter()` が呼ばれると、内部的には `Array` ではなく、`arr.constructor` を使用して新しい結果の配列を作成します。結果に対してさらに `PowerArray` が持つメソッドを使用し続けることができるため、これは非常に有用です。 + +さらに、その振る舞いをカスタマイズすることも可能です。 + +特別な静的な getter `Symbol.species` を追加することができます。これが存在する場合、`map` や `filter` などの場合に Javascript が内部的に使用するコンストラクタを返す必要があります。 + +もし、`map` や `filter` のような組み込みのメソッドが通常の配列を返してほしい場合、次のように `Symbol.species` で `Array` を返すようにします: + +```js run +class PowerArray extends Array { + isEmpty() { + return this.length === 0; + } + +*!* + // 組み込みのメソッドは、これをコンストラクタとして使います + static get [Symbol.species]() { + return Array; + } +*/!* +} + +let arr = new PowerArray(1, 2, 5, 10, 50); +alert(arr.isEmpty()); // false + +// filter はコンストラクタとして arr.constructor[Symbol.species] を使って新しい配列を作ります +let filteredArr = arr.filter(item => item >= 10); + +*!* +// filteredArr は PowerArray ではなく Array です +*/!* +alert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty is not a function +``` + +ご覧の通り、これで `.filter` は `Array` を返します。そのため、拡張機能はこれ以上渡されません。 + +```smart header="他のコレクションも同様に動作します" +`Map` や `Set` などの他のコレクションも同様に動作します。これらも `Symbol.species` を使用しています。 +``` + +## 組み込みでは、静的の継承はありません + +組み込みのオブジェクトは独自の静的メソッドを持っています。例えば `Object.keys`, `Array.isArray` などです。 + +既にご存知のように、ネイティブクラスは互いに拡張しあいます。例えば、 `Array` は `Object` を拡張しています。 + +通常、あるクラスが別のクラスを拡張する場合、静的メソッド、非静的メソッドの両方が継承されます。これは記事 [](info:static-properties-methods#statics-and-inheritance) で説明しました。 + +しかし、組み込みのクラスは例外です。組み込みのクラスはお互いから静的なプロパティを継承しません。 + +例えば、`Array` と `Date` はともに `Object` を継承しているので、これらのインスタンスは `Object.prototype` のメソッドを持ちます。が、`Array.[[Prototype]]` は `Object` を参照しないため、例えば `Array.keys()` (or `Date.keys()`) と言った静的メソッドは存在しません。 + +これは、`Date` と `Object` の構造のイメージです: + +![](object-date-inheritance.svg) + +`Date` と `Object` の間に繋がりはないことに注目してください。`Object` と `Date` は両方とも独立し、`Date.prototype` は `Object.prototype` を継承しているだけです。 + +これは組み込みのオブジェクトの継承と `extends` をして得られるものとの比較における重要な違いです。 diff --git a/1-js/09-classes/05-extend-natives/object-date-inheritance.svg b/1-js/09-classes/05-extend-natives/object-date-inheritance.svg new file mode 100644 index 0000000000..be47d7fd96 --- /dev/null +++ b/1-js/09-classes/05-extend-natives/object-date-inheritance.svg @@ -0,0 +1 @@ +constructor: Object toString: function hasOwnProperty: function ...Object.prototypeconstructor: Date toString: function getDate: function ...Date.prototypeObjectDatenew Date()[[Prototype]][[Prototype]]prototypeprototypedefineProperty keys ...now parse ...1 Jan 2019 \ No newline at end of file diff --git a/1-js/07-object-oriented-programming/11-instanceof/1-strange-instanceof/solution.md b/1-js/09-classes/06-instanceof/1-strange-instanceof/solution.md similarity index 100% rename from 1-js/07-object-oriented-programming/11-instanceof/1-strange-instanceof/solution.md rename to 1-js/09-classes/06-instanceof/1-strange-instanceof/solution.md diff --git a/1-js/07-object-oriented-programming/11-instanceof/1-strange-instanceof/task.md b/1-js/09-classes/06-instanceof/1-strange-instanceof/task.md similarity index 100% rename from 1-js/07-object-oriented-programming/11-instanceof/1-strange-instanceof/task.md rename to 1-js/09-classes/06-instanceof/1-strange-instanceof/task.md diff --git a/1-js/09-classes/06-instanceof/article.md b/1-js/09-classes/06-instanceof/article.md new file mode 100644 index 0000000000..8c4768bb62 --- /dev/null +++ b/1-js/09-classes/06-instanceof/article.md @@ -0,0 +1,218 @@ +# クラスのチェック: "instanceof" + +`instanceof` 演算子でオブジェクトが特定のクラスに属しているのかを確認することができます。また、継承も考慮されます。 + +このようなチェックが必要なケースは多々あるかもしれません。ここでは、その型に応じて引数を別々に扱う *多形(ポリモーフィック)* 関数を構築するために使用します。 + +## instanceof 演算子 [#ref-instanceof] + +構文は次の通りです: +```js +obj instanceof Class +``` + +それは `obj` が `Class` (または、それを継承しているクラス)に属している場合に `true` を返します。 + +例: + +```js run +class Rabbit {} +let rabbit = new Rabbit(); + +// Rabbit クラスのオブジェクト? +*!* +alert( rabbit instanceof Rabbit ); // true +*/!* +``` + +コンストラクタ関数でも動作します。: + +```js run +*!* +// class の代わり +function Rabbit() {} +*/!* + +alert( new Rabbit() instanceof Rabbit ); // true +``` + +...また `Array` のような組み込みクラスでも動作します。: + +```js run +let arr = [1, 2, 3]; +alert( arr instanceof Array ); // true +alert( arr instanceof Object ); // true +``` + +`arr` は `Object` クラスにも属していることに留意してください。`Array` はプロトタイプ的に `Object` を継承しているためです。 + +通常、`instanceof` 演算子はチェックのためにプロトタイプチェーンを検査します。この動きに対して、静的メソッド `Symbol.hasInstance` でカスタムロジックが設定できます。 + +`obj instanceof Class` のアルゴリズムはおおまかに次のように動作します。: + +1. もし静的メソッド `Symbol.hasInstance` があれば、それ(`Class[Symbol.hasInstance](obj)`)を使います。これは `true` または `false` を返す必要があり、これで `instanceof` の振る舞いがカスタマイズできます。: + + 例: + + ```js run + // catEat プロパティをもつものは animal と想定する + // instanceOf チェックを設定 + class Animal { + static [Symbol.hasInstance](obj) { + if (obj.canEat) return true; + } + } + + let obj = { canEat: true }; + + alert(obj instanceof Animal); // true: Animal[Symbol.hasInstance](obj)が呼ばれます + ``` + +2. ほとんどのクラスは `Symbol.hasInstance` を持っていません。このケースでは、通常のロジックが使用されます: `obj instanceOf Class` は `Class.prototype` が `obj` のプロトタイプチェーンうちの1つと等しいかをチェックします。 + + 言い換えると、以下のような比較を行います: + ```js + obj.__proto__ == Class.prototype + obj.__proto__.__proto__ == Class.prototype + obj.__proto__.__proto__.__proto__ == Class.prototype + ... + // いずれかが true の場合、 true が返却されます + // そうでない場合、チェーンの末尾に到達すると false を返します + ``` + + 上の例では、``rabbit.__proto__ === Rabbit.prototype` なので、すぐに回答が得られます。 + + 継承のケースでは、2つめのステップでマッチします: + + ```js run + class Animal {} + class Rabbit extends Animal {} + + let rabbit = new Rabbit(); + *!* + alert(rabbit instanceof Animal); // true + */!* + + // rabbit.__proto__ == Rabbit.prototype + *!* + // rabbit.__proto__.__proto__ === Animal.prototype (match!) + */!* + ``` + +これは、`rabbit instanceof Animal` と `Animal.prototype` を比較したものです。: + +![](instanceof.svg) + +ところで、[objA.isPrototypeOf(objB)](mdn:js/object/isPrototypeOf) というメソッドもあります。これは `objA` が `objB` のプロトタイプチェーンのどこかにあれば `true` を返します。なので、`obj instanceof Class` のテストは `Class.prototype.isPrototypeOf(obj)` と言い換えることができます。 + +面白いことに、`Class` コンストラクタ自身はチェックには参加しません! プロトタイプと `Class.prototype`のチェーンだけです。 + +これは `prototype` が変更されたときに興味深い結果につながります。 + +このように: + +```js run +function Rabbit() {} +let rabbit = new Rabbit(); + +// prototype を変更します +Rabbit.prototype = {}; + +// ...もう rabbit ではありません +*!* +alert( rabbit instanceof Rabbit ); // false +*/!* +``` + +## おまけ: 型のための Object toString + +私たちは通常の文字列は `[object Object]` という文字列に変換されることをすでに知っています。: + +```js run +let obj = {}; + +alert(obj); // [object Object] +alert(obj.toString()); // 同じ +``` + +これが `toString` の実装です。しかし、実際にはそれよりもはるかに強力な `toString` を作る隠れた機能があります。それを拡張させて `typeof` または `instanceof` の代替として利用することができます。 + +奇妙に聞こえますか?たしかに。分かりやすく説明しましょう。 + +[スペック(specification)](https://tc39.github.io/ecma262/#sec-object.prototype.tostring)によって、組み込みの `toString` はオブジェクトから抽出し、任意の値のコンテキストで実行することができます。そして、その結果はその値に依存します。 + +- 数値の場合、それは `[object Number]` になります。 +- 真偽値の場合、`[object Boolean]` になります。 +- `null` の場合: `[object Null]` +- `undefined` の場合: `[object Undefined]` +- 配列の場合: `[object Array]` +- ...など (カスタマイズ可能). + +デモを見てみましょう: + +```js run +// 使いやすくするために toString メソッドを変数にコピー +let objectToString = Object.prototype.toString; + +// これの型はなに? +let arr = []; + +alert( objectToString.call(arr) ); // [object Array] +``` + +ここでは、コンテキスト `this=arr` で関数 `objectToString` を実行するため、[デコレータと転送, call/apply](info:call-apply-decorators) の章で説明した [call](mdn:js/function/call) を使いました。 + +内部的には、`toString` アルゴリズムは `this` を検査し、対応する結果を返します。ほかの例です。: + +```js run +let s = Object.prototype.toString; + +alert( s.call(123) ); // [object Number] +alert( s.call(null) ); // [object Null] +alert( s.call(alert) ); // [object Function] +``` + +### Symbol.toStringTag + +Object `toString` の振る舞いは特別なオブジェクトプロパティ `Symbol.toStringTag` を使用してカスタマイズできます。 + +例: + +```js run +let user = { + [Symbol.toStringTag]: 'User' +}; + +alert( {}.toString.call(user) ); // [object User] +``` + +ほとんどの環境固有のオブジェクトには、このようなプロパティがあります。これはいくつかのブラウザ固有の例です。: + +```js run +// 環境固有のオブジェクトとクラスのtoStringTag: +alert( window[Symbol.toStringTag]); // window +alert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest + +alert( {}.toString.call(window) ); // [object Window] +alert( {}.toString.call(new XMLHttpRequest()) ); // [object XMLHttpRequest] +``` + +ご覧の通り、結果は正確に `Symbol.toStringTag` (存在する場合)で、`[object ...]` の中にラップされています。 + +最終的には、プリミティブなデータ型だけでなく、組み込みオブジェクトのためにも機能し、カスタマイズすることもできる "強化された typeof" があります。 + +これは、型を文字列として取得するだけでなく、チェックするために、組み込みオブジェクトに対して `instanceof` の代わりに使用できます。 + +## サマリ + +私たちが知っている型チェックメソッドについて再確認しましょう: + +| | 対象 | 戻り値 | +|---------------|-------------|---------------| +| `typeof` | プリミティブ | 文字列 | +| `{}.toString` | プリミティブ, 組み込みオブジェクト, `Symbol.toStringTag` をもつオブジェクト | 文字列 | +| `instanceof` | オブジェクト | true/false | + +ご覧のように、`{}.toString` は技術的には "より高度な" `typeof` です。 + +そして、`instanceof` 演算子は、クラス階層を扱っていて継承を考慮したクラスのチェックをしたい場合に本当に輝きます。 diff --git a/1-js/09-classes/06-instanceof/instanceof.svg b/1-js/09-classes/06-instanceof/instanceof.svg new file mode 100644 index 0000000000..d63b03a8a2 --- /dev/null +++ b/1-js/09-classes/06-instanceof/instanceof.svg @@ -0,0 +1 @@ +Animal.prototypeObject.prototypeRabbit.prototype[[Prototype]]rabbit[[Prototype]][[Prototype]]null[[Prototype]]= Animal.prototype? \ No newline at end of file diff --git a/1-js/09-classes/07-mixins/article.md b/1-js/09-classes/07-mixins/article.md new file mode 100644 index 0000000000..8709ab8399 --- /dev/null +++ b/1-js/09-classes/07-mixins/article.md @@ -0,0 +1,208 @@ +# ミックスイン + +JavaScriptでは、単一のオブジェクトからのみ継承できます。オブジェクトの `[[Prototype]]` は1つしかありません。そしてクラスは単一の他のクラスだけを拡張することができます。 + +ですが、それを制限と感じる場面があります。例えば、`StreetSweeper` と `Bicycle` というクラスを持っていて、`StreetSweepingBicycle` を作りたい場合などです。 + +あるいは、`User` クラスと、イベント生成を実装した `EventEmitter` クラスがあり、`EventEmitter` の機能性を `User` に追加し、user がイベントを emit できるようにしたい、などです。 + +ここでは、それを助ける "mixins(ミックスイン)" と呼ばれる考え方があります。 + +Wikipedis の定義によると、[mixin](https://en.wikipedia.org/wiki/Mixin) は他のクラスの親クラスでないが、他のクラスで使用するためのメソッドを含むクラスです。 + +つまり、*mixin* は特定の振る舞いを実装したメソッドを提供しますが、単独では使わず、別のクラスの振る舞いを追加するために使います。 + +## mixin の例 + +JavaScriptで mixin を作る最もシンプルな方法は、役立つメソッドをもつオブジェクトを作ることです。そうすることで、それらを簡単にどのクラスのプロトタイプにもマージできます。 + +例えば、ここでは mixin `sayHiMixin` は `User` のためのいくつかの "スピーチ" を追加するために使われます。: + +```js run +*!* +// mixin +*/!* +let sayHiMixin = { + sayHi() { + alert("Hello " + this.name); + }, + sayBye() { + alert("Bye " + this.name); + } +}; + +*!* +// 使い方: +*/!* +class User { + constructor(name) { + this.name = name; + } +} + +// メソッドをコピー +Object.assign(User.prototype, sayHiMixin); + +// これで User は sayHi できます +new User("Dude").sayHi(); // Hi Dude! +``` + +これは継承ではなく、単純なメソッドのコピーです。従って、`User` は他のクラスを拡張することができ、さらに以下のように追加のメソッドをミックスインするとして含めることができます: + +```js +class User extends Person { + // ... +} + +Object.assign(User.prototype, sayHiMixin); +``` + +ミックスインは自身の内部で継承を活用することもできます。 + +例えば、ここでは `sayHiMixin` は `sayMixin` を継承しています。: + +```js run +let sayMixin = { + say(phrase) { + alert(phrase); + } +}; + +let sayHiMixin = { + __proto__: sayMixin, // (またはここで prototype を設定するのに Object.create が使えます) + + sayHi() { + *!* + // 親のメソッド呼び出し + */!* + super.say("Hello " + this.name); // (*) + }, + sayBye() { + super.say("Bye " + this.name); // (*) + } +}; + +class User { + constructor(name) { + this.name = name; + } +} + +// メソッドをコピー +Object.assign(User.prototype, sayHiMixin); + +// これで User は sayHi できます +new User("Dude").sayHi(); // Hello Dude! +``` + +`sayHiMixin` からの親メソッド `super.say()` の呼び出し(`(*)` でラベル付けされた行)は、クラスではなくそのミックスインのプロトタイプの中のメソッドを探すことに注意してください。 + +これがその図です(右の部分を見てください): + +![](mixin-inheritance.svg) + +これは、メソッド `sayHi` と `sayBye` は最初に `sayHiMixin` で作成されたためです。そのため、たとえコピーされたとしても、上の図で示す通り、`[[HomeObject]]` 内部プロパティは `sayHiMixin` を参照します。 + +`super` は `[[HomeObject]].[[Prototype]]` で親メソッドを探すので、これは `User.[[Prototype]]` ではなく、`sayHiMixin.[[Prototype]]` を探すことを意味します。 + +## イベントMixin + +さて、実践のためのミックスインを作ってみましょう。 + +多くのブラウザオブジェクト(例えば)の重要な特徴は、イベントを生成できることです。イベントは、それを必要とするものへ "情報をブロードキャスト" する優れた方法です。そのため、簡単にイベントに関連する関数を任意の class/object に追加できるよう mixin を作成しましょう。 + +- mixin はなにか重要なことが起こったときに、"イベントを生成" するためのメソッド `.trigger(name, [...data])` を提供します。`name` 引数はイベント名で、オプションでイベントデータを含む追加の引数が続きます。 +- また、指定された名前のイベントのリスナーとして `handler` 関数を追加するメソッド `.on(name, handler)` も提供します。指定された `name` のイベントがトリガーされたときに呼ばれ、`.trigger` 呼び出しから引数と取得します。 +- そして、`handler` リスナーを削除するためのメソッド `.off(name, handler)`。 + +この mixin を追加したあと、オブジェクト `user` は、訪問者がログインするときに、`"login"` イベントを生成することができるようになります。また、別のオブジェクト、例えば `calendar` はそのようなイベントをリッスンし、ログインした人のカレンダーを読み込みます。 + +あるいは、`menu` はメニュー項目が選択されたときにイベント `"select"` を生成でき、他のオブジェクトはそのイベントに反応するためにハンドラを割り当てることができます。 + +これはそのコードです: + +```js run +let eventMixin = { + /** + * イベントの購読, 使い方: + * menu.on('select', function(item) { ... } + */ + on(eventName, handler) { + if (!this._eventHandlers) this._eventHandlers = {}; + if (!this._eventHandlers[eventName]) { + this._eventHandlers[eventName] = []; + } + this._eventHandlers[eventName].push(handler); + }, + + /** + * 購読のキャンセル 使い方: + * menu.off('select', handler) + */ + off(eventName, handler) { + let handlers = this._eventHandlers && this._eventHandlers[eventName]; + if (!handlers) return; + for(let i = 0; i < handlers.length; i++) { + if (handlers[i] == handler) { + handlers.splice(i--, 1); + } + } + }, + + /** + * イベントを生成してデータをアタッチ + * this.trigger('select', data1, data2); + */ + trigger(eventName, ...args) { + if (!this._eventHandlers || !this._eventHandlers[eventName]) { + return; // イベントに対応するハンドラがない場合 + } + + // ハンドラ呼び出し + this._eventHandlers[eventName].forEach(handler => handler.apply(this, args)); + } +}; +``` + + +1. `.on(eventName, handler)` -- その名前のイベントが発生した時に実行するための関数 `handler` を割り当てます。ハンドラは `_eventHandlers` プロパティの中に格納されます。 +2. `.off(eventName, handler)` -- ハンドラリストから関数を削除します。 +3. `.trigger(eventName, ...args)` -- イベントを生成します: すべての割り当てられたハンドラが呼び出され、`args` がそれらの引数として渡されます。 + +使い方: + +```js run +// クラスを作成 +class Menu { + choose(value) { + this.trigger("select", value); + } +} +// mixin を追加 +Object.assign(Menu.prototype, eventMixin); + +let menu = new Menu(); + +// 選択時にハンドラを呼び出し +*!* +menu.on("select", value => alert("Value selected: " + value)); +*/!* + +// イベントのトリガ => 上のハンドラを実行し次を表示 +// Value selected: 123 +menu.choose("123"); // 選択された値 +``` + +これで、もしユーザ選択に反応するためのコードがある場合、`menu.on(...)` でバインドすることができます。 + +そして、`eventMixin` は継承のチェーンを邪魔することなく、我々が望むだけのクラスに対してこのような振る舞いを追加することができます。 + +## サマリ + +*Mixin(ミックスイン)* -- は一般的なオブジェクト指向プログラミングの言葉です: 他のクラスのためのメソッドを含むクラスです。 + +いくつかの他の言語は多重継承をサポートしています。JavaScriptは多重継承をサポートしていませんが、プロトタイプにそれらをコピーすることでミックスインが実装できます。 + +上で見てきたイベントハンドリングのように、複数の振る舞いを追加することでクラスを拡張する方法としてミックスインが利用できます。 + +ミックスインで誤って既存のクラスメソッドを上書きすると、競合が発生する可能性があります。そのため、一般的には、このような可能性を最小化するためにも、ミックスインの命名についてよく考える必要があります。 diff --git a/1-js/07-object-oriented-programming/13-mixins/head.html b/1-js/09-classes/07-mixins/head.html similarity index 100% rename from 1-js/07-object-oriented-programming/13-mixins/head.html rename to 1-js/09-classes/07-mixins/head.html diff --git a/1-js/09-classes/07-mixins/mixin-inheritance.svg b/1-js/09-classes/07-mixins/mixin-inheritance.svg new file mode 100644 index 0000000000..1fdc223936 --- /dev/null +++ b/1-js/09-classes/07-mixins/mixin-inheritance.svg @@ -0,0 +1 @@ +sayHi: function sayBye: functionsayHiMixinsay: functionsayMixin[[Prototype]]constructor: User sayHi: function sayBye: functionUser.prototype[[Prototype]]name: ...user[[HomeObject] \ No newline at end of file diff --git a/1-js/09-classes/index.md b/1-js/09-classes/index.md new file mode 100644 index 0000000000..dfe284a989 --- /dev/null +++ b/1-js/09-classes/index.md @@ -0,0 +1 @@ +# クラス diff --git a/1-js/08-error-handling/1-try-catch/1-finally-or-code-after/solution.md b/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md similarity index 100% rename from 1-js/08-error-handling/1-try-catch/1-finally-or-code-after/solution.md rename to 1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md diff --git a/1-js/08-error-handling/1-try-catch/1-finally-or-code-after/task.md b/1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md similarity index 100% rename from 1-js/08-error-handling/1-try-catch/1-finally-or-code-after/task.md rename to 1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md diff --git a/1-js/10-error-handling/1-try-catch/article.md b/1-js/10-error-handling/1-try-catch/article.md new file mode 100644 index 0000000000..417961962a --- /dev/null +++ b/1-js/10-error-handling/1-try-catch/article.md @@ -0,0 +1,675 @@ +# エラーハンドリング, "try..catch" + +どんなに我々のプログラミングが素晴らしくても、スクリプトがエラーになることはあります。それはミス、予期しないユーザ入力、間違ったサーバレスポンスやその他多くの理由により発生する可能性があります。 + +通常、エラーが発生するとスクリプトは "死" (即時に停止) に、それをコンソールに出力します。 + +しかし、エラーを "キャッチ" し、死ぬ代わりにより意味のあることをする構文構造 `try..catch` があります。 + +## "try..catch" 構文 + +`try..catch` 構造は2つのメインブロックを持っています: `try` と `catch` です。: + +```js +try { + + // code... + +} catch (err) { + + // error handling + +} +``` + +それは次のように動作します: + +1. まず、`try {...}` のコードが実行されます。 +2. エラーがなければ、`catch(err)` は無視されます: 実行が `try` の最後に到達した後、`catch` を飛び越えます。 +3. エラーが発生した場合、`try` の実行が停止し、コントロールフローは `catch(err)` の先頭になります。`err` 変数(任意の名前が使えます)は発生した事象に関する詳細をもつエラーオブジェクトを含んでいます。 + +![](try-catch-flow.svg) + +従って、`try {…}` ブロックの内側のエラーはスクリプトを殺しません: `catch` の中でそれを扱う機会が持てます。 + +いくつか例を見てみましょう。 + +- エラーなしの例: `alert` `(1)` と `(2)` を表示します: + + ```js run + try { + + alert('Start of try runs'); // *!*(1) <--*/!* + + // ...ここではエラーはありません + + alert('End of try runs'); // *!*(2) <--*/!* + + } catch(err) { + + alert('Catch is ignored, because there are no errors'); // (3) + + } + ``` +- エラーの例: `(1)` と `(3)` を表示します: + + ```js run + try { + + alert('Start of try runs'); // *!*(1) <--*/!* + + *!* + lalala; // エラー, 変数は宣言されていません! + */!* + + alert('End of try (never reached)'); // (2) + + } catch(err) { + + alert(`Error has occured!`); // *!*(3) <--*/!* + + } + ``` + + +````warn header="`try..catch` は実行時エラーにのみ作用します" +`try..catch` を動作させるために、コードは実行可能でなければなりません。つまり、有効なJavaScriptである必要があります。 + +もしコードが構文的に誤っている場合には動作しません。例えば次は角括弧の不一致です: + +```js run +try { + {{{{{{{{{{{{ +} catch(e) { + alert("The engine can't understand this code, it's invalid"); +} +``` + +JavaScriptエンジンは最初にコードを読み、次にそれを実行します。読み込みのフェーズで発生したエラーは "解析時間(parse-time)" エラーと呼ばれ、回復不能です(コードの内部からは)。なぜなら、エンジンはそのコードを理解することができないからです。 + +そのため、`try..catch` は有効なコードの中で起きたエラーのみを扱うことができます。このようなエラーは "ランタイムエラー" または "例外" と呼ばれます。 +```` + + +````warn header="`try..catch` は同期的に動作します" +もし `setTimeout` の中のような "スケジュールされた" コードで例外が発生した場合、`try..catch` はそれをキャッチしません。: + +```js run +try { + setTimeout(function() { + noSuchVariable; // スクリプトはここで死にます + }, 1000); +} catch (e) { + alert( "won't work" ); +} +``` + +`try..catch` は実際には関数をスケジュールする `setTimeout` 呼び出しをラップするためです。しかし関数自身は後で実行され、その時エンジンはすでに `try..catch` 構造を抜けています。 + +スケジュールされた関数の内側の例外をキャッチするためには、その関数の中に `try..catch` が必要です。: +```js run +setTimeout(function() { + try { + noSuchVariable; // try..catch がエラーをハンドリングします! + } catch (e) { + alert( "error is caught here!" ); + } +}, 1000); +``` +```` + +## エラーオブジェクト + +エラーが発生したとき、JavaScript はその詳細を含めたオブジェクトを生成します。そして `catch` の引数として渡されます。: + +```js +try { + // ... +} catch(err) { // <-- "エラーオブジェクト", err の代わりに別の名前を使うこともできます + // ... +} +``` + +すべての組み込みのエラーに対して、`catch` ブロック内のエラーオブジェクトは2つの主なプロパティを持っています。: + +`name` +: エラー名です。未定義変数の場合、それは `"ReferenceError"` です。 + +`message` +: エラー詳細に関するテキストメッセージです。 + +ほとんどの環境では、その他非標準のプロパティが利用可能です。最も広く使われ、サポートされているのは以下です: + +`stack` +: 現在のコールスタックです: エラーに繋がったネスト呼び出しのシーケンスに関する情報を持つ文字列です。デバッグ目的で使われます。 + +例: + +```js run untrusted +try { +*!* + lalala; // エラー, 変数が宣言されていません! +*/!* +} catch(err) { + alert(err.name); // ReferenceError + alert(err.message); // lalala is not defined + alert(err.stack); // ReferenceError: lalala is not defined at (...スタック呼び出し) + + // 全体としてエラーを表示する事もできます + // エラーは "name: message" として文字列に変換されます + alert(err); // ReferenceError: lalala is not defined +} +``` + +## 任意の "catch" バインディング + +[recent browser=new] + +エラーの詳細が必要ない場合、`catch` はそれを省略できます: + +```js +try { + // ... +} catch { // <-- (err) なし + // ... +} +``` + +## "try..catch" の利用 + +`try..catch` の実際のユースケースについて探索してみましょう。 + +既にご存知の通り、JavaScriptは JSONエンコードされた値を読むためのメソッド [JSON.parse(str)](mdn:js/JSON/parse) がサポートされています。 + +通常、それはネットワーク経由でサーバまたは別のソースから受信したデータをデコードするために使われます。 + +今、次のようにデータを受信し、`JSON.parse` を呼び出します。: + +```js run +let json = '{"name":"John", "age": 30}'; // サーバからのデータ + +*!* +let user = JSON.parse(json); // テキスト表現をJSオブジェクトに変換 +*/!* + +// 今、 user 文字列からプロパティを持つオブジェクトです +alert( user.name ); // John +alert( user.age ); // 30 +``` + +JSON に関する詳細な情報は、チャプター を参照してください。 + +**`json` が不正な形式の場合、`JSON.parse` はエラーになるのでスクリプトは "死にます"。** + +それで満足しますか?もちろん満足しません! + +この方法だと、もしデータが何か間違っている場合、訪問者はそれを知ることができません(開発者コンソールを開かない限り)。また、人々は、エラーメッセージなしで何かが "単に死んでいる" ことを本当に本当に嫌います。 + +エラーを扱うために `try..catch` を使いましょう。: + +```js run +let json = "{ bad json }"; + +try { + +*!* + let user = JSON.parse(json); // <-- エラーが起きたとき... +*/!* + alert( user.name ); // 動作しません + +} catch (e) { +*!* + // ...実行はここに飛びます + alert( "Our apologies, the data has errors, we'll try to request it one more time." ); + alert( e.name ); + alert( e.message ); +*/!* +} +``` + +ここでは、メッセージを表示するためのだけに `catch` ブロックを使っていますが、より多くのことをすることができます。: 新たなネットワーク要求、訪問者への代替手段の提案、ロギング機構へエラーに関する情報の送信... すべて、単に死ぬよりははるかに良いです。 + +## 独自のエラーをスローする + +仮に `json` が構文的に正しいが、必須の `"name"` プロパティを持っていない場合どうなるでしょう? + +このように: + +```js run +let json = '{ "age": 30 }'; // 不完全なデータ + +try { + + let user = JSON.parse(json); // <-- エラーなし +*!* + alert( user.name ); // name はありません! +*/!* + +} catch (e) { + alert( "doesn't execute" ); +} +``` + +ここで、`JSON.parse` は通常どおり実行しますが、`"name"` の欠落は実際には我々にとってはエラーです。 + +エラー処理を統一するために、`throw` 演算子を使います。 + +### "Throw" 演算子 + +`throw` 演算子はエラーを生成します。 + +構文は次の通りです: + +```js +throw +``` + +技術的には、エラーオブジェクトとしてなんでも使うことができます。たとえ、数値や文字列のようなプリミティブでもOKです。しかし、`name` と `message` プロパティを持つオブジェクトを使うのがベターです(組み込みのエラーと互換性をいくらか保つために)。 + +JavaScriptは標準エラーのための多くの組み込みのコンストラクタを持っています: `Error`, `SyntaxError`, `ReferenceError`, `TypeError` などです。我々もエラーオブジェクトを作るのにそれらが使えます。 + +構文は次の通りです: + +```js +let error = new Error(message); +// or +let error = new SyntaxError(message); +let error = new ReferenceError(message); +// ... +``` + +組み込みのエラー(任意のオブジェクトではなく、エラーのみ)では、`name` プロパティはコンストラクタの名前と全く同じになります。そして `message` は引数から取られます。 + +例: + +```js run +let error = new Error("Things happen o_O"); + +alert(error.name); // Error +alert(error.message); // Things happen o_O +``` + +`JSON.parse` が生成するエラーの種類を見てみましょう: + +```js run +try { + JSON.parse("{ bad json o_O }"); +} catch(e) { +*!* + alert(e.name); // SyntaxError +*/!* + alert(e.message); // Unexpected token o in JSON at position 0 +} +``` + +ご覧の通り、それは `SyntaxError` です。 + +...そして、我々のケースでは、ユーザは必ず `"name"` を持っていると仮定するので、`name` の欠落もまた構文エラーとして扱います。 + +なので、それをスローするようにしましょう: + +```js run +let json = '{ "age": 30 }'; // 不完全なデータ + +try { + + let user = JSON.parse(json); // <-- エラーなし + + if (!user.name) { +*!* + throw new SyntaxError("Incomplete data: no name"); // (*) +*/!* + } + + alert( user.name ); + +} catch(e) { + alert( "JSON Error: " + e.message ); // JSON Error: Incomplete data: no name +} +``` + +行 `(*)` で、`throw` 演算子が与えられた `message` で `SyntaxError` を生成します。それは JavaScript が生成するのと同じ方法です。`try` の実行はすぐに停止し、制御フローは `catch` に移ります。 + +今や、`catch` は`JSON.parse` と他のケースすべてのエラーハンドリングのための1つの場所になりました。 + +## 再スロー + +上の例で、私たちは不正なデータを処理するために `try..catch` を使っています。しかし、`try {...}` ブロックの中で *別の予期しないエラー* が発生する可能性はあるでしょうか? 変数が未定義、またはその他、単に "不正なデータ" ではない何か。 + +例: + +```js run +let json = '{ "age": 30 }'; // 不完全なデータ + +try { + user = JSON.parse(json); // <-- user の前に "let" をつけ忘れた + + // ... +} catch(err) { + alert("JSON Error: " + err); // JSON Error: ReferenceError: user is not defined + // (実際にはJSONのエラーではありません) +} +``` + +もちろん、すべての可能性があります! プログラマはミスをするものです。何十年も何百万人もの人が使っているオープンソースのユーティリティであっても、突然酷いバグが発見され、ひどいハッキングにつながることがあります( `ssh` ツールで起こったようなものです)。 + +私たちのケースでは、`try..catch` は "不正なデータ" エラーをキャッチすることを意図しています。しかし、その性質上、`catch` は `try` からの *すべての* エラーを取得します。ここでは予期しないエラーが発生しますが、同じ `"JSON Error"` メッセージが表示されます。これは誤りでコードのデバッグをより難しくします。 + +このような問題を避けるため、"再スロー" という手法が利用できます。ルールはシンプルです: + +**キャッチはそれが知っているエラーだけを処理し、すべてのオブジェクトを "再スロー" するべきです** + +"再スロー" テクニックの詳細は次のように説明できます: + +1. すべてのエラーをキャッチします。 +2. `catch(err) {...}` ブロックで、エラーオブジェクト `err` を解析します。 +3. どう処理すればいいか分からなければ、`throw err` をします。 + +通常は、`instanceof` 演算子を使用してエラーの種類がチェックできます。: + +```js run +try { + user = { /*...*/ }; +} catch (err) { +*!* + if (err instanceof ReferenceError) { +*/!* + alert('ReferenceError'); // 未定義変数へのアクセスに対する "ReferenceError" + } +} +``` + +また、`erro.name` プロパティから、エラークラス名を取得することも可能です。すべてのネイティブエラーはエラークラス名があります。もう1つの選択肢は、`err.constructor.name` を参照することです。 + +下のコードでは、`catch` が `SyntaxError` だけを処理するよう再スローを使っています。: + +```js run +let json = '{ "age": 30 }'; // 不完全なデータ +try { + + let user = JSON.parse(json); + + if (!user.name) { + throw new SyntaxError("Incomplete data: no name"); + } + +*!* + blabla(); // 予期しないエラー +*/!* + + alert( user.name ); + +} catch(e) { + +*!* + if (e.name == "SyntaxError") { + alert( "JSON Error: " + e.message ); + } else { + throw e; // 再スロー (*) + } +*/!* + +} +``` + +行 `(*)` での、`catch` ブロック内部からのエラーのスローは `try..catch` を "抜けて" 外部の `try..catch` 構造(存在する場合)でキャッチされる、またはスクリプトをキルします。 + +従って、`catch` ブロックは実際に扱い方を知っているエラーだけを処理しその他すべてを "スキップ" します。 + +下の例は、このようなエラーが1つ上のレベルの `try..catch` で捕捉されるデモです: + +```js run +function readData() { + let json = '{ "age": 30 }'; + + try { + // ... +*!* + blabla(); // error! +*/!* + } catch (e) { + // ... + if (e.name != 'SyntaxError') { +*!* + throw e; // 再スロー (今のエラーの扱い方を知らない) +*/!* + } + } +} + +try { + readData(); +} catch (e) { +*!* + alert( "External catch got: " + e ); // caught it! +*/!* +} +``` + +ここでは、`readData` は `SyntaxError` の処理の仕方だけ知っており、外部の `try..catch` はすべての処理の方法を知っています。 + +## try..catch..finally + +待ってください、それですべてではありません。 + +`try..catch` 構造はもう1つのコード句: `finally` を持つ場合があります。 + +もし存在する場合、それはすべてのケースで実行します。: + +- エラーが無かった場合は、`try` の後で。 +- エラーがあった場合には `catch` の後で。 + +拡張された構文はこのようになります。: + +```js +*!*try*/!* { + ... コードを実行しようとします ... +} *!*catch*/!*(e) { + ... エラーを処理します ... +} *!*finally*/!* { + ... 常に実行します ... +} +``` + +このコードを実行してみましょう。: + +```js run +try { + alert( 'try' ); + if (confirm('Make an error?')) BAD_CODE(); +} catch (e) { + alert( 'catch' ); +} finally { + alert( 'finally' ); +} +``` + +このコードは2つの実行方法があります。: + +1. もし "Make an error" に "Yes" と答えると、`try -> catch -> finally` となります。 +2. もし "No" と言えば、`try -> finally` となります。 + +`finally` 句は `try..catch` の前に何かを開始して、どのような結果であれファイナライズをしたいときに頻繁に使われます。 + +例えば、フィボナッチ数関数 `fib(n)` にかかる時間を計測したいとします。当然ながら、それを実行する前に計測を開始して、実行後に終了させることができます。しかし、仮に関数呼び出しの間でエラーが起きたらどうなるでしょう?特に下のコードの `fib(n)` の実装では、負の値または非整数値だとエラーを返します。 + +`finally` 句は何があっても計測を完了させるのに良い場所です。 + +ここで、`finally` は両方のシチュエーション -- `fib` の実行が成功するケースと失敗するケース -- で時間が正しく計測されることを保証します。: + +```js run +let num = +prompt("Enter a positive integer number?", 35) + +let diff, result; + +function fib(n) { + if (n < 0 || Math.trunc(n) != n) { + throw new Error("Must not be negative, and also an integer."); + } + return n <= 1 ? n : fib(n - 1) + fib(n - 2); +} + +let start = Date.now(); + +try { + result = fib(num); +} catch (e) { + result = 0; +*!* +} finally { + diff = Date.now() - start; +} +*/!* + +alert(result || "error occured"); + +alert( `execution took ${diff}ms` ); +``` + +コードを実行して `prompt` に `35` を入力することで確認できます -- 通常 `try` の後に `finally` を実行します。そして `-1` を入れると -- すぐにエラーになり、その実行は `0ms` となります。両方の計測は正しく行われています。 + +つまり、関数を終了するには方法が2つあります: `return` または `throw` です。 `finally` 句はそれら両方とも処理します。 + + +```smart header="変数は `try..catch..finally` の内部でローカルです" +上のコードで `result` と `diff` 変数は `try..catch` の *前* で宣言されていることに注意してください。 + +そうでなく、`let` が `{...}` ブロックの中で作られている場合、その中でしか見えません。 +``` + +````smart header="`finally` と `return`" +Finally 句は `try..catch` からの *任意の* 終了に対して機能します。それは明白な `return` も含みます。 + +下の例では、`try` の中で `return` があります。この場合、`finally` は制御が外部コードに戻る前に実行されます。 + +```js run +function func() { + + try { +*!* + return 1; +*/!* + + } catch (e) { + /* ... */ + } finally { +*!* + alert( 'finally' ); +*/!* + } +} + +alert( func() ); // 最初に finally の alert が動作し、次にこれが動作します +``` +```` + +````smart header="`try..finally`" + +`catch` 句がない `try..catch` 構造も役立ちます。私たちはここでエラーを正しく処理したくないが、開始した処理が完了したことを確認したいときに使います。 + +```js +function func() { + // (計測など)完了させる必要のあるなにかを開始する + try { + // ... + } finally { + // すべてが死んでいても完了させる + } +} +``` +上のコードでは、`try` の内側のエラーは常に抜けます。なぜなら `catch` がないからです。しかし `finally` は実行フローが外部に移る前に機能します。 +```` + +## グローバルな catch + +```warn header="環境特有" +このセクションの情報はコアなJavaScriptの一部ではありません。 +``` + +`try..catch` の外側で致命的なエラーが起きてスクリプトが死んだことをイメージしてください。プログラミングエラーやその他何か酷いものによって。 + +そのような出来事に反応する方法はありますか? エラーをログに記録したり、ユーザーに何かを見せたり(通常はエラーメッセージが表示されません)。 + +仕様ではそのようなものはありませんが、通常、環境がそれを提供しています。なぜなら本当に有用だからです。例えば、Node.js はそのために [process.on('uncaughtException')](https://nodejs.org/api/process.html#process_event_uncaughtexception)を持っています。また、ブラウザでは関数を特別な [window.onerror](mdn:api/GlobalEventHandlers/onerror) プロパティに代入することができます。それはキャッチしていないエラーの場合に実行されます。 + +構文: + +```js +window.onerror = function(message, url, line, col, error) { + // ... +}; +``` + +`message` +: エラーメッセージ + +`url` +: エラーが起きたスクリプトのURL + +`line`, `col` +: エラーが起きた行と列番号 + +`error` +: エラーオブジェクト + +例: + +```html run untrusted refresh height=1 + +``` + +グローバルハンドラー `window.onerror` の役割は、通常スクリプトの実行の回復ではありません -- プログラミングエラーの場合、恐らくそれは不可能なので開発者にエラーメッセージを送ります。 + +このようなケースでエラーログを提供する web サービスもあります。https://errorception.com> や 。 + +それらは次のように動きます: + +1. 私たちはサービスに登録し、ページに挿入するためのJSのピース(またはスクリプトのURL)をそれらから得ます。 +2. そのJSスクリプトはカスタムの `window.onerror` 関数を持っています。 +3. エラーが起きた時、そのサービスへネットワークリクエストを送ります。 +4. 私たちはサービスのWebインタフェースにログインしてエラーを見ることができます。 + +## サマリ + +`try..catch` 構造はランタイムエラーを処理することができます。文字通りコードを実行しようと試みて、その中で起こるエラーをキャッチします。 + +構文は次の通りです: + +```js +try { + // コードを実行 +} catch(err) { + // エラーが起きた場合、ここにジャンプ + // err はエラーオブジェクト +} finally { + // すべてのケースで try/catch 後に実行する +} +``` + +`catch` セクションがない、または `finally` がない場合があります。なので `try..catch` と `try..finally` もまた有効です。 + +エラーオブジェクトは次のプロパティを持っています。: + +- `message` -- 人が読めるエラーメッセージです。 +- `name` -- エラー名を指す文字列です(エラーコンストラクタ名) +- `stack` (非標準) -- エラー生成時のスタックです。 + +エラーオブジェクトが不要であれば、`catch (err) {` の代わりに `catch {` とすることで省略できます。 + +また、`throw` 演算子を使って独自のエラーを生成することもできます。技術的には、`throw` の引数は何でもよいですが、通常は組み込みの `Error` クラスを継承しているエラーオブジェクトです。次のチャプターでエラーを拡張する方法について詳しく説明します。 + +*再スロー* はエラーハンドリングの非常に重要なパターンです。: `catch` ブロックは通常、特定のエラータイプを処理する方法を予期し、知っています。したがって、知らないエラーは再スローすべきです。 + +たとえ `try..catch` を持っていない場合でも、ほとんどの環境では "抜け出た" エラーをキャッチするために "グローバル" なエラーハンドラを設定することができます。ブラウザでは、それは `window.onerror` です。 diff --git a/1-js/10-error-handling/1-try-catch/try-catch-flow.svg b/1-js/10-error-handling/1-try-catch/try-catch-flow.svg new file mode 100644 index 0000000000..2c0d71348c --- /dev/null +++ b/1-js/10-error-handling/1-try-catch/try-catch-flow.svg @@ -0,0 +1 @@ +BeginNo ErrorsAn error occured in the codeIgnore catch blockIgnore the rest of tryExecute catch blocktry { }// code... \ No newline at end of file diff --git a/1-js/08-error-handling/2-custom-errors/1-format-error/solution.md b/1-js/10-error-handling/2-custom-errors/1-format-error/solution.md similarity index 100% rename from 1-js/08-error-handling/2-custom-errors/1-format-error/solution.md rename to 1-js/10-error-handling/2-custom-errors/1-format-error/solution.md diff --git a/1-js/08-error-handling/2-custom-errors/1-format-error/task.md b/1-js/10-error-handling/2-custom-errors/1-format-error/task.md similarity index 100% rename from 1-js/08-error-handling/2-custom-errors/1-format-error/task.md rename to 1-js/10-error-handling/2-custom-errors/1-format-error/task.md diff --git a/1-js/10-error-handling/2-custom-errors/article.md b/1-js/10-error-handling/2-custom-errors/article.md new file mode 100644 index 0000000000..242cd26484 --- /dev/null +++ b/1-js/10-error-handling/2-custom-errors/article.md @@ -0,0 +1,330 @@ +# カスタムエラー, Error の拡張 + +開発する際、タスクで上手くいかない可能性のある特定の物事を反映するために、独自のエラークラスが必要になることがよくあります。ネットワーク操作のエラーであれば、`HttpError`、データベース操作は `DbError`、検索操作の場合は`NotFoundError` などが必要な場合があります。 + +エラーは `message`, `name` 、望ましくは `stack` のような基本のエラープロパティをサポートすべきです。ですが、他にも独自のプロパティを持つかもしれません。例えば `HttpError` オブジェクトであれば、 `404`, `403` もしくは `500` といった値をとる `statusCode` プロパティです。 + +JavaScript は任意の引数で `throw` できるので、技術的にはカスタムのエラークラスは `Error` から継承する必要はありません。しかし、継承しているとエラーオブジェクトを識別する `obj instanceof Error` を使えるようになります。そのため、継承しておくほうのがベターです。 + +アプリケーションを開発するにつれ、独自のエラーが自然に階層を形成します。たとえば、 `HttpTimeoutError` は `HttpError` を継承する、といったように。 + +## Error を拡張する + +例として、ユーザデータをもつ JSON を読む関数 `readUser(json)` を考えてみましょう。 + +ここでは、有効な `json` がどのように見えるかの例を示します。: +```js +let json = `{ "name": "John", "age": 30 }`; +``` + +内部的には、`JSON.parse` を使います。不正な `json` を受け取った場合は `SyntaxError` をスローします。しかし、たとえ `json` が構文的に正しくても、それが正しいユーザとは限りません。その `json` には必要なデータが不足しているかもしれません。例えば、上のケースだと、ユーザに必要不可欠な `name` や `age` プロパティを持っていない場合です。 + +私たちの関数 `readUser(json)` はJSONを読むだけでなく、データのチェック(バリデート)をします。必須のフィールドがなかったり、フォーマットが誤っている場合はエラーになります。そしてそれは `SyntaxError` ではありません。なぜならデータは構文的には正しく、別の種類のエラーだからです。したがって、このエラーは `ValidationError` と呼び、そのためのクラスを作りましょう。このようなエラーは、問題のあるフィールドに関する情報も保持する必要があります。 + +`ValidationError` クラスは `Error` クラスから継承します。 + +`Error` クラスは組み込みですが、ここでは何を拡張しようとしているのか理解できるよう、そのクラスのおおよそのコードを示します: + +```js +// JavaScript自体で定義された組み込みのErrorクラスの「擬似コード」 +class Error { + constructor(message) { + this.message = message; + this.name = "Error"; // (組み込みのエラークラスごとに異なる名前) + this.stack = ; // 非標準ですが、ほとんどの環境はサポートしています + } +} +``` + +では、`ValidationError` をそれから継承させ、動かしましょう: + +```js run untrusted +*!* +class ValidationError extends Error { +*/!* + constructor(message) { + super(message); // (1) + this.name = "ValidationError"; // (2) + } +} + +function test() { + throw new ValidationError("Whoops!"); +} + +try { + test(); +} catch(err) { + alert(err.message); // Whoops! + alert(err.name); // ValidationError + alert(err.stack); // それぞれの行番号を持つネストされたコールのリスト +} +``` + +補足: `(1)` で親のコンストラクタを呼び出しています。JavaScriptは子のコンストラクタ内で `super` 呼び出しが必要で、これは義務です。親のコンストラクタは `message` プロパティをセットします。 + +親のコンストラクタは `name` プロパティも `"Error"` へセットしますので、行 `(2)` で正しい値にリセットしています。 + +`readUser(json)` で使ってみましょう: + +```js run +class ValidationError extends Error { + constructor(message) { + super(message); + this.name = "ValidationError"; + } +} + +// Usage +function readUser(json) { + let user = JSON.parse(json); + + if (!user.age) { + throw new ValidationError("No field: age"); + } + if (!user.name) { + throw new ValidationError("No field: name"); + } + + return user; +} + +// try..catch での動作例 + +try { + let user = readUser('{ "age": 25 }'); +} catch (err) { + if (err instanceof ValidationError) { +*!* + alert("Invalid data: " + err.message); // Invalid data: No field: name +*/!* + } else if (err instanceof SyntaxError) { // (*) + alert("JSON Syntax Error: " + err.message); + } else { + throw err; // 知らないエラーなので、再スロー (**) + } +} +``` + +上のコードの `try..catch` ブロックは `ValidationError` と `JSON.parse` からの組み込みの `SyntaxError` 両方を処理します。 + +行 `(*)` で特定のエラーの種類をチェックするために、どのように `instanceof` を使っているか見てください。 + +このようにして `err.name` を見ることもできます。: + +```js +// ... +// (err instanceof SyntaxError) の代わり +} else if (err.name == "SyntaxError") { // (*) +// ... +``` + +`instanceof` の方がよりベターです。なぜなら、将来 `ValidationError` を拡張し、`PropertyRequiredError` のようなサブタイプを作るからです。そして `instanceof` チェックは新しい継承したクラスでもうまく機能し続けます。それは将来を保証します。 + +また、`catch` が未知のエラーに遭遇したとき、行 `(**)` でそれを再度スローすることも重要です。 `catch` はバリデーションと構文エラーの処理の仕方だけを知っています。他の種類(コード中のタイポやその他)の場合は失敗します。 + +## さらなる継承 + +`ValidationError` クラスはとても汎用的です。色んな種類の誤りがあります。例えば、プロパティが存在しなかったり、誤ったフォーマット(`age` が文字列値のような)など。ここで、存在しないプロパティに対するより具体的なクラス `PropertyRequiredError` を作りましょう。欠落しているプロパティについての追加の情報を保持します。 + +```js run +class ValidationError extends Error { + constructor(message) { + super(message); + this.name = "ValidationError"; + } +} + +*!* +class PropertyRequiredError extends ValidationError { + constructor(property) { + super("No property: " + property); + this.name = "PropertyRequiredError"; + this.property = property; + } +} +*/!* + +// 使用法 +function readUser(json) { + let user = JSON.parse(json); + + if (!user.age) { + throw new PropertyRequiredError("age"); + } + if (!user.name) { + throw new PropertyRequiredError("name"); + } + + return user; +} + +// try..catch での動作例 + +try { + let user = readUser('{ "age": 25 }'); +} catch (err) { + if (err instanceof ValidationError) { +*!* + alert("Invalid data: " + err.message); // Invalid data: No property: name + alert(err.name); // PropertyRequiredError + alert(err.property); // name +*/!* + } else if (err instanceof SyntaxError) { + alert("JSON Syntax Error: " + err.message); + } else { + throw err; // 知らないエラーなので、それを再スロー + } +} +``` + +新しいクラス `PropertyRequiredError` は簡単に使えます。プロパティ名を渡すだけです。: `new PropertyRequiredError(property)`。人が読める `message` はコンストラクタで作られます。 + +`PropertyRequiredError` コンストラクタでの `this.name` は再度手動で割り当てられることに注目してください。カスタムのエラーを作る度に `this.name = ` と代入することには、少しうんざりするかもしれません。が、`this.name = this.constructor.name` を行う "基本エラー" クラスを作り、カスタムエラーはそれを継承することで避けることができます。 + +これを `MyError` と呼びましょう。 + +ここでは、単純化した `MyError` のコードと他のカスタムエラークラスを示します。: + +```js run +class MyError extends Error { + constructor(message) { + super(message); +*!* + this.name = this.constructor.name; +*/!* + } +} + +class ValidationError extends MyError { } + +class PropertyRequiredError extends ValidationError { + constructor(property) { + super("No property: " + property); + this.property = property; + } +} + +// name is correct +alert( new PropertyRequiredError("field").name ); // PropertyRequiredError +``` + +これで、カスタムエラーははるかに短くなりました。特に `ValidationError` はコンストラクタの `"this.name = ..."` の行を除いたので。 + +## 例外のラッピング + +上のコードの関数 `readUser` の目的は "ユーザデータを読むこと" ですよね?この処理では異なる種類のエラーが起こる可能性があります。今は `SyntaxError` と `ValidationError` を持っていますが、将来 `readUser` 関数が成長し、新たなコードが別の種類のエラーを生み出すかもしれません。 + +`readUser` を呼び出すコードは、これらのエラーを処理する必要があります。今は `catch` ブロックの中で、異なるエラータイプのチェックと未知のエラーを再スローするために、複数の `if` を使っています。 + +スキーマはこのようになります: + +```js +try { + ... + readUser() // 潜在的なエラーの原因 + ... +} catch (err) { + if (err instanceof ValidationError) { + // バリデーションエラーの処理 + } else if (err instanceof SyntaxError) { + // シンタックスエラーの処理 + } else { + throw err; // 未知のエラー、再スロー + } +} +``` + +上のコードでは2種類のエラーがありますが、他にもありえます。 + +もし `readUser` 関数が複数の種類のエラーを生成する場合、`readUser` 呼び出しをするすべてのコードで、毎回本当にすべてのエラータイプを1つずつチェックしたいですか? + +多くの場合、答えは、"いいえ" です。: 外側のコードは "それらすべての1つ上のレベル" でありたいです。つまり、"データ読み込みエラー" が発生したかどうかが知りたいだけです。発生した正確な理由については、多くの場合は無関心です(エラーメッセージで説明されています)。また、さらに良いのは、必要な場合にのみエラーの詳細を取得する方法があることです。 + +このテクニックは "例外のラッピング(wrapping exceptions)" と呼ばれます。 + +1. 汎用的な "データ読み込み" エラーを表現する新しいクラス `ReadError` を作ります。 +2. 関数 `readUser` は `ValidationError` や `SyntaxError` のような、内部で発生するデータ読み込みエラーをキャッチし、代わりに `ReadError` を生成します。 +3. `ReadError` オブジェクトは自身の `cause` プロパティに元のエラーへの参照を保持します。 + +`readUser` を呼び出すコードは、`ReadError` だけをチェックする必要があり、他の種類のデータ読み込みエラーのチェックは不要です。そして、エラーの詳細が必要な場合は、その `cause` プロパティで確認できます。 + +これは、`ReadError` を定義し、`readUser` と `try..catch` でそれを利用するデモです。: + +```js run +class ReadError extends Error { + constructor(message, cause) { + super(message); + this.cause = cause; + this.name = 'ReadError'; + } +} + +class ValidationError extends Error { /*...*/ } +class PropertyRequiredError extends ValidationError { /* ... */ } + +function validateUser(user) { + if (!user.age) { + throw new PropertyRequiredError("age"); + } + + if (!user.name) { + throw new PropertyRequiredError("name"); + } +} + +function readUser(json) { + let user; + + try { + user = JSON.parse(json); + } catch (err) { +*!* + if (err instanceof SyntaxError) { + throw new ReadError("Syntax Error", err); + } else { + throw err; + } +*/!* + } + + try { + validateUser(user); + } catch (err) { +*!* + if (err instanceof ValidationError) { + throw new ReadError("Validation Error", err); + } else { + throw err; + } +*/!* + } + +} + +try { + readUser('{bad json}'); +} catch (e) { + if (e instanceof ReadError) { +*!* + alert(e); + // Original error: SyntaxError: Unexpected token b in JSON at position 1 + alert("Original error: " + e.cause); +*/!* + } else { + throw e; + } +} +``` + +上のコードで、`readUser` は説明されている通りに正確に動作します -- 構文とバリデーションエラーをキャッチし、`ReadError` エラーを代わりにスローします(未知のエラーは通常通り再スローします)。 + +なので、外部のコードは `instanceof ReadError` をチェックするだけです。可能性のあるすべてのエラータイプをリストする必要はありません。 + +このアプローチは、"低レベルの例外" を取り除き、呼び出しコードで使用するより抽象的で便利な "ReadError" に "ラップ" するため、"例外のラッピング" と呼ばれます。 オブジェクト指向プログラミングで広く使用されています。 + +## サマリ + +- `Error` や他の組み込みのエラークラスから継承することができます。そのときは、`name` プロパティに手を入れることと、`super` の呼び出しを忘れないでください。 +- 特定のエラーのチェックには `instanceof` が使えます。これは継承している場合にも有効です。しかし、ときにはサードパーティのライブラリから来たエラーオブジェクトを持っていて、簡単にそのクラスを取得する方法がないことがあります。このような場合には `name` プロパティが使えます。 +- 例外のラッピングは広く利用されているテクニックです: 関数が低レベルの例外を処理し、それぞれの低レベルの例外の代わりに高レベルのエラーを生成します。低レベルの例外は、上の例の `err.cause` のようにオブジェクトのプロパティになることがありますが、厳密には必須ではありません。 diff --git a/1-js/08-error-handling/index.md b/1-js/10-error-handling/index.md similarity index 100% rename from 1-js/08-error-handling/index.md rename to 1-js/10-error-handling/index.md diff --git a/1-js/11-async/01-callbacks/article.md b/1-js/11-async/01-callbacks/article.md new file mode 100644 index 0000000000..7a282d6744 --- /dev/null +++ b/1-js/11-async/01-callbacks/article.md @@ -0,0 +1,310 @@ + + +# 前置き: コールバック + +```warn header="ここの例ではブラウザメソッドを使用します" +コールバック、promise や他の抽象的な概念の使用法を示すために、いくつかのブラウザメソッドを使用します。具体的には、スクリプトの読み込みと単純なドキュメント操作の実行です。 + +これらのメソッドに馴染みがなく、例での使用法がわかりにくい場合、チュートリアルの [次のパート](/document) のいくつかの章を読むことをおすすめします。 + + +``` + +多くの関数は *非同期* アクションがスケジュールできる JavaScript ホスト環境によって提供されます。つまり、今開始したアクションですが、後で終了します。 + +例えば、そのような関数の1つとして `setTimeout` 関数があります。 + +非同期アクションの実際の例は他にもあります。例えばスクリプトとモジュールの読み込みです(後述の章で説明します)。 + +指定された `src` でスクリプトをロードする関数 `loadScript(src)` を見てください。: + +```js +function loadScript(src) { + // + + diff --git a/6-async/04-promise-api/one.js b/1-js/11-async/04-promise-error-handling/one.js similarity index 100% rename from 6-async/04-promise-api/one.js rename to 1-js/11-async/04-promise-error-handling/one.js diff --git a/1-js/11-async/04-promise-error-handling/promise-then-chain.svg b/1-js/11-async/04-promise-error-handling/promise-then-chain.svg new file mode 100644 index 0000000000..fb60142fbd --- /dev/null +++ b/1-js/11-async/04-promise-error-handling/promise-then-chain.svg @@ -0,0 +1 @@ +.thennew Promiseresolve(1)return 2.thenreturn 4.then \ No newline at end of file diff --git a/1-js/11-async/04-promise-error-handling/promise-then-many.svg b/1-js/11-async/04-promise-error-handling/promise-then-many.svg new file mode 100644 index 0000000000..8fea7beaaf --- /dev/null +++ b/1-js/11-async/04-promise-error-handling/promise-then-many.svg @@ -0,0 +1 @@ +.thennew Promiseresolve(1).then.then \ No newline at end of file diff --git a/1-js/11-async/04-promise-error-handling/three.js b/1-js/11-async/04-promise-error-handling/three.js new file mode 100644 index 0000000000..8536e85a18 --- /dev/null +++ b/1-js/11-async/04-promise-error-handling/three.js @@ -0,0 +1,3 @@ +function three() { + alert(3); +} diff --git a/6-async/04-promise-api/two.js b/1-js/11-async/04-promise-error-handling/two.js similarity index 100% rename from 6-async/04-promise-api/two.js rename to 1-js/11-async/04-promise-error-handling/two.js diff --git a/6-async/04-promise-api/iliakan.json b/1-js/11-async/04-promise-error-handling/user.json similarity index 100% rename from 6-async/04-promise-api/iliakan.json rename to 1-js/11-async/04-promise-error-handling/user.json diff --git a/6-async/04-promise-api/01-promise-errors-as-results/solution.md b/1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.md similarity index 100% rename from 6-async/04-promise-api/01-promise-errors-as-results/solution.md rename to 1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.md diff --git a/6-async/04-promise-api/01-promise-errors-as-results/solution.view/index.html b/1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.view/index.html similarity index 100% rename from 6-async/04-promise-api/01-promise-errors-as-results/solution.view/index.html rename to 1-js/11-async/05-promise-api/01-promise-errors-as-results/solution.view/index.html diff --git a/6-async/04-promise-api/01-promise-errors-as-results/source.view/index.html b/1-js/11-async/05-promise-api/01-promise-errors-as-results/source.view/index.html similarity index 100% rename from 6-async/04-promise-api/01-promise-errors-as-results/source.view/index.html rename to 1-js/11-async/05-promise-api/01-promise-errors-as-results/source.view/index.html diff --git a/6-async/04-promise-api/01-promise-errors-as-results/task.md b/1-js/11-async/05-promise-api/01-promise-errors-as-results/task.md similarity index 100% rename from 6-async/04-promise-api/01-promise-errors-as-results/task.md rename to 1-js/11-async/05-promise-api/01-promise-errors-as-results/task.md diff --git a/6-async/02-promise-basics/03-animate-circle-promise/solution.md b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.md similarity index 100% rename from 6-async/02-promise-basics/03-animate-circle-promise/solution.md rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.md diff --git a/6-async/04-promise-api/02-promise-errors-as-results-2/solution.view/index.html b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.view/index.html similarity index 100% rename from 6-async/04-promise-api/02-promise-errors-as-results-2/solution.view/index.html rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/solution.view/index.html diff --git a/6-async/04-promise-api/02-promise-errors-as-results-2/source.view/index.html b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/source.view/index.html similarity index 100% rename from 6-async/04-promise-api/02-promise-errors-as-results-2/source.view/index.html rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/source.view/index.html diff --git a/6-async/04-promise-api/02-promise-errors-as-results-2/task.md b/1-js/11-async/05-promise-api/02-promise-errors-as-results-2/task.md similarity index 100% rename from 6-async/04-promise-api/02-promise-errors-as-results-2/task.md rename to 1-js/11-async/05-promise-api/02-promise-errors-as-results-2/task.md diff --git a/1-js/11-async/05-promise-api/article.md b/1-js/11-async/05-promise-api/article.md new file mode 100644 index 0000000000..abf602707c --- /dev/null +++ b/1-js/11-async/05-promise-api/article.md @@ -0,0 +1,323 @@ +# Promise API + +`Promise` クラスには 6 つの静的メソッドがあります。ここでそれらのユースケースについて簡単に説明します。 + +## Promise.all + +並列に複数の promise を実行し、すべてが準備できるまで待ちたいとしましょう。 + +例えば、平行で複数の URL をダウンロードし、すべてが完了したらコンテンツを処理する場合です。 + +これが `Promise.all` の目的です。 + +構文は次の通りです: + +```js +let promise = Promise.all(iterable); +``` + +`Promise.all` は iterable(反復可能, 通常は promise の配列)を取り、新しい promise を返します。 + +新しい promise は、配列の各 promise がすべて解決され、それらの結果を配列に持つと解決(resolve)されます。 + +例えば、下の `Promise.all` は 3 秒後に解決され、その後結果は配列 `[1, 2, 3]` です。: + +```js run +Promise.all([ + new Promise((resolve, reject) => setTimeout(() => resolve(1), 3000)), // 1 + new Promise((resolve, reject) => setTimeout(() => resolve(2), 2000)), // 2 + new Promise((resolve, reject) => setTimeout(() => resolve(3), 1000)) // 3 +]).then(alert); // 1,2,3 promise が準備できた時: 各 promise は配列の中身に寄与します +``` + +結果の配列要素の順番は元の promise の順番と同じであることに注意してください。たとえ1つ目の promise が解決まで最も時間がかかったとしても、結果の配列の中では先頭にあります。 + +一般的なやり方は、処理データの配列を promise の配列にマップし、それを `Promise.all` にラップすることです。 + +例えば、URL の配列がある場合、次のようにしてすべてをフェッチできます: + +```js run +let urls = [ + 'https://api.github.com/users/iliakan', + 'https://api.github.com/users/remy', + 'https://api.github.com/users/jeresig' +]; + +// 各 url を promise の fetch(github url) へマップする +let requests = urls.map(url => fetch(url)); + +// Promise.all はすべてのジョブが解決されるまで待ちます +Promise.all(requests) + .then(responses => responses.forEach( + response => alert(`${response.url}: ${response.status}`) + )); +``` + +名前からGithubユーザのユーザ情報を取得するより大きな例です(id で商品の配列をフェッチできますが、ロジックは同じです): + +```js run +let names = ['iliakan', 'remy', 'jeresig']; + +let requests = names.map(name => fetch(`https://api.github.com/users/${name}`)); + +Promise.all(requests) + .then(responses => { + // すべてのレスポンスが用意できたら HTTP ステータスコードが見られます + for(let response of responses) { + alert(`${response.url}: ${response.status}`); // 各 url で 200 が表示されます + } + + return responses; + }) + // それぞれの中身を読むために、レスポンスの配列を response.json() の配列にマッピングします + .then(responses => Promise.all(responses.map(r => r.json()))) + // すべての JSON応答が解析され、"user" はそれらの配列です。 + .then(users => users.forEach(user => alert(user.name))); +``` + +**いずれかの promise が reject された場合、`Promise.all` により返却された promise は即座にエラーと一緒に reject します。** + +例: + +```js run +Promise.all([ + new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)), +*!* + new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)), +*/!* + new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)) +]).catch(alert); // Error: Whoops! +``` + +ここでは、2つ目の promise が2秒後に reject されます。それは即座に `Promise.all` の reject に繋がるため、`catch` が実行されます。: reject されたエラーは `Promise.all` 全体の結果になります。 + +```warn header="エラー時の場合, 他の promise は無視されます" +ある promise が reject がされると、`Promise.all` は即座に reject し、配列の他の promise を完全に忘れます。これらの結果は無視されます。 + +例えば、上の例のように複数の `fetch` 呼び出しがあり、1つが失敗した場合、他の promise は依然として実行中ですが、`Promise.all` はこれ以上ウォッチしません。おそらく正常に解決されますが、結果は無視されます。 + +`promise` には "キャンセル" の考え方はないので、`Promise.all` はキャンセルしません。[別の章](info:fetch-abort)では、これに役立つ `AbortController` について説明しています。がこれは Promise API の一部ではありません。 +``` + +````smart header="`Promise.all(iterable)` は `iterable` の中で非 promise の項目を許可します" +通常、`Promise.all(iterable)` は promise の iterable (ほとんどの場合は配列)を受け付けます。しかし、もしそれらのオブジェクトが promise ではない場合、`Promise.resolve` でラップします。 + +例えば、ここでは結果は `[1, 2, 3]` になります: + +```js run +Promise.all([ + new Promise((resolve, reject) => { + setTimeout(() => resolve(1), 1000) + }), + 2, // Promise.resolve(2) と扱われる + 3 // Promise.resolve(3) と扱われる +]).then(alert); // 1, 2, 3 +``` + +したがって、必要に応じて準備済みの値を `Promise.all` に渡せます。 +```` + +## Promise.allSettled + +[recent browser="new"] + +`Promise.all` はいずれかの promise が reject されると、全体として reject されます。これは "白か黒か" のケースにはよいです。続行するために *すべての* 成功した結果が必要な場合です: + +```js +Promise.all([ + fetch('/template.html'), + fetch('/style.css'), + fetch('/data.json') +]).then(render); // render メソッドはすべての fetch の結果が必要 +``` + +`Promise.allSettled` は結果に関わらずすべての promise が解決するまで待ちます。結果の配列は以下を持ちます: + +- 成功したレスポンスの場合: `{status:"fulfilled", value:result}` +- エラーの場合: `{status:"rejected", reason:error}` + +例えば、複数のユーザに対する情報をフェッチしたいとします。たとえ1リクエストが失敗しても、他の結果はほしいです。 + +`Promise.allSettled` を使いましょう: + +```js run +let urls = [ + 'https://api.github.com/users/iliakan', + 'https://api.github.com/users/remy', + 'https://no-such-url' +]; + +Promise.allSettled(urls.map(url => fetch(url))) + .then(results => { // (*) + results.forEach((result, num) => { + if (result.status == "fulfilled") { + alert(`${urls[num]}: ${result.value.status}`); + } + if (result.status == "rejected") { + alert(`${urls[num]}: ${result.reason}`); + } + }); + }); +``` + +上の行 `(*)` の `results` は以下になります: +```js +[ + {status: 'fulfilled', value: ...レスポンス...}, + {status: 'fulfilled', value: ...レスポンス...}, + {status: 'rejected', reason: ...エラーオブジェクト...} +] +``` + +そのため、各 promise に対してステータスと `値 or エラー` を取得します。 + +### Polyfill + +ブラウザでは、`Promise.allSettled` をサポートしていません。が polyfill するのは簡単です: + +```js +if (!Promise.allSettled) { + const rejectHandler = reason => ({ status: 'rejected', reason }); + + const resolveHandler = value => ({ status: 'fulfilled', value }); + + Promise.allSettled = function (promises) { + const convertedPromises = promises.map(p => Promise.resolve(p).then(resolveHandler, rejectHandler)); + return Promise.all(convertedPromises); + }; +} +``` + +このコードで、`promises.map` は入力値を取り、`p => Promise.resolve(p)` でそれらを promise に変換(非 promise が渡された場合に備えて)し、`.then` ハンドラに追加します。 + +ハンドラは成功した結果 `value` を `{status:'fulfilled', value}` に、エラー `reason` は `{status:'rejected', reason}` に変換します。これは `Promise.allSettled` のフォーマットです。 + +これで、指定された *すべて* の promise の結果を得る(たとえいくつかが reject されても) `Promise.allSettled` が利用できます。 + +## Promise.race + +これは `Promise.all` と同様ですが、すべてが完了するのを待つのではなく、最初の結果(またはエラー)のみを待ちます。 + +構文です: + +```js +let promise = Promise.race(iterable); +``` + +例えば、ここでは結果は `1` になります: + +```js run +Promise.race([ + new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)), + new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)), + new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)) +]).then(alert); // 1 +``` + +なので、最初の結果/エラーが `Promise.race` 全体の結果になります。最初の確定した promise が "レースに勝った" 後、それ以外の結果/エラーは無視されます。 + + +## Promise.any + +`Promise.race` と同様ですが、最初の履行した(fulfilled) promise のみを待ち、その結果を得ます。指定された promise がすべて reject された場合、返却された promise は [`AggregateError`](mdn:js/AggregateError) で reject されます(`errors` プロパティにすべての promise エラーを格納する特別なエラーオブジェクトです)。 + +構文は次の通りです: + +```js +let promise = Promise.any(iterable); +``` + +例えば、ここでは結果は `1` になります: + +```js run +Promise.any([ + new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 1000)), + new Promise((resolve, reject) => setTimeout(() => resolve(1), 2000)), + new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)) +]).then(alert); // 1 +``` + +最初の promise が一番はやいですが、reject されたため、2つ目の promise が結果になっています。最初の履行した(fulfilled)promise が "レースに勝ち"、他の結果はすべて無視されます。 + +これは promise がすべて失敗した例です: + +```js run +Promise.any([ + new Promise((resolve, reject) => setTimeout(() => reject(new Error("Ouch!")), 1000)), + new Promise((resolve, reject) => setTimeout(() => reject(new Error("Error!")), 2000)) +]).catch(error => { + console.log(error.constructor.name); // AggregateError + console.log(error.errors[0]); // Error: Ouch! + console.log(error.errors[1]); // Error: Error! +}); +``` + +ご覧の通り、失敗した promise のエラーオブジェクトは `AggregateError` オブジェクトの `errors` プロパティで利用可能です。 + +## Promise.resolve/reject + +最近のコードでは、`Promise.resolve` と `Promise.reject` メソッドはめったに必要とされません。`async/await` 構文([少し後](info:async-await)で説明します) によって、これらがやや時代遅れになるためです。 + +ここでは完全性のためと、何らかの理由で `async/await` が使用できない場合のために説明します。 + +### Promise.resolve + +`Promise.resolve(value)` は結果 `value` をもつ解決された promise を作成します。 + +以下と同様です: + +```js +let promise = new Promise(resolve => resolve(value)); +``` + +このメソッドは、関数が promise を返すことが期待される場合の互換性のために使用されます。 + +例えば、以下の `loadCached` 関数は、URL をフェッチし、コンテンツを記憶(キャッシュ)します。同じURLに対する将来の呼び出し時には、キャッシュから即座にコンテンツが返されますが、promise にするために `Promise.resolve` を使用します。これで戻り値は常に promise になります: + +```js +let cache = new Map(); + +function loadCached(url) { + if (cache.has(url)) { +*!* + return Promise.resolve(cache.get(url)); // (*) +*/!* + } + + return fetch(url) + .then(response => response.text()) + .then(text => { + cache.set(url,text); + return text; + }); +} +``` + +関数は promise を返すことが保証されているため、`loadCached(url).then(…)` と書くことができます。`loadCached` の後に常に `.then` が使用でき、これが行 `(*)` の `Promise.resolve` の目的です。 + +### Promise.reject + +`Promise.reject(error)` は `error` を持つ reject された promise を作成します。 + +以下と同じです: + +```js +let promise = new Promise((resolve, reject) => reject(error)); +``` + +実際、このメソッドはほとんど使われません。 + +## サマリ + +`Promise` クラスには 6 つの静的なメソッドがあります。: + +1. `Promise.all(promises)` -- すべての promise が解決するのを待って、結果の配列を返します。与えられた promise のいずれかが拒否されると、 `Promise.all` のエラーとなり、他のすべての結果は無視されます。 +2. `Promise.allSettled(promises)` (最近追加されたメソッド) -- すべての promise が完了するのを待って、以下のオブジェクト配列として結果を返します: + - `status`: `"fulfilled"` or `"rejected"` + - `value` (fulfilled の場合) or `reason` (rejected の場合). +3. `Promise.race(promises)` -- 最初の promise の解決を待ち、その結果/エラーを返します。 +4. `Promise.any(promises)` (最近追加されたメソッド) -- 最初に履行された(fulfilled) promise を待ち、その結果を返します。指定したすべての promise が reject された場合、[`AggregateError`](mdn:js/AggregateError) が `Promise.any` のエラーになります。 +5. `Promise.resolve(value)` -- 与えられた値で promise を解決(resolve)します +6. `Promise.reject(error)` -- 与えられたエラーで promise を拒否(reject)します + +これらのうち、`Promise.all` が実践では最も一般的です。 diff --git a/6-async/04-promise-api/head.html b/1-js/11-async/05-promise-api/head.html similarity index 100% rename from 6-async/04-promise-api/head.html rename to 1-js/11-async/05-promise-api/head.html diff --git a/1-js/11-async/05-promise-api/iliakan.json b/1-js/11-async/05-promise-api/iliakan.json new file mode 100644 index 0000000000..32f89971a8 --- /dev/null +++ b/1-js/11-async/05-promise-api/iliakan.json @@ -0,0 +1,4 @@ +{ + "name": "iliakan", + "isAdmin": true +} diff --git a/1-js/11-async/05-promise-api/one.js b/1-js/11-async/05-promise-api/one.js new file mode 100644 index 0000000000..948a60e075 --- /dev/null +++ b/1-js/11-async/05-promise-api/one.js @@ -0,0 +1,3 @@ +function one() { + alert(1); +} diff --git a/1-js/11-async/05-promise-api/two.js b/1-js/11-async/05-promise-api/two.js new file mode 100644 index 0000000000..b04795b86c --- /dev/null +++ b/1-js/11-async/05-promise-api/two.js @@ -0,0 +1,3 @@ +function two() { + alert(2); +} diff --git a/1-js/11-async/06-promisify/article.md b/1-js/11-async/06-promisify/article.md new file mode 100644 index 0000000000..415f476231 --- /dev/null +++ b/1-js/11-async/06-promisify/article.md @@ -0,0 +1,132 @@ +# Promisification + +"Promisification" は単純な変換を表す長い用語です。コールバックを受け付ける関数から、Promise を返す関数への変換です。 + +多くの関数やライブラリはコールバックベースなので、このような変換は実際しばしば必要とされます。Promise はより便利であるため、このような変換は理にかなっています。 + +より理解するために例を見てみましょう。 + +例えば、章 の `loadScript(src, callback)` を考えてみましょう。 + +```js run +function loadScript(src, callback) { + let script = document.createElement('script'); + script.src = src; + + script.onload = () => callback(null, script); + script.onerror = () => callback(new Error(`Script load error for ${src}`)); + + document.head.append(script); +} + +// 使用例: +// loadScript('path/script.js', (err, script) => {...}) +``` + +関数は指定された `src` のスクリプトを読み込み、エラーの場合は `callback(err)`, 読み込みに成功した場合には `callback(null, script)` を呼び出します。こちらは以前みましたが、このフォーマットはコールバックの使用で広く合意されているものです。 + +Promise 化してみましょう。 + +新しい `loadScriptPromise(src)` 関数は同じことをしますが、コールバックの代わりに Promise を返します。 + +つまり、`src` のみ(`callback` なし)を渡し、戻り値で promise を得ます。この promise は読み込みが成功すると `script` で resolve し、そうでなければエラーで reject します。 + +こちらです: +```js +let loadScriptPromise = function(src) { + return new Promise((resolve, reject) => { + loadScript(src, (err, script) => { + if (err) reject(err) + else resolve(script); + }); + }) +} + +// 使用例: +// loadScriptPromise('path/script.js').then(...) +``` + +ご覧の通り、新しい関数は元の `loadScript` 関数のラッパーです。結果を Promise の `resolve/reject` に変換する独自のコールバックを提供し、呼び出します。 + +これで `loadScriptPromise` は Promise ベースのコードによくフィットします。コールバックよりも promise の方がよい(この後その理由をみていきます)なら、代わりにこちらを利用します。 + +実際には、複数の関数を promise 化する必要があるかもしれません。この場合はヘルパーを用意するのが便利です。 + +`promisify(f)` は Promise 化する関数 `f` を引数に取り、ラッパー関数を返します。 + +```js +function promisify(f) { + return function (...args) { // ラッパー関数を返します + return new Promise((resolve, reject) => { + function callback(err, result) { // f のためのカスタムコールバック + if (err) { + return reject(err); + } else { + resolve(result); + } + } + + args.push(callback); // 引数の末尾にカスタムコールバックを追加 + + f.call(this, ...args); // 元の関数を呼び出します + }); + }; +}; + +// 使用例: +let loadScriptPromise = promisify(loadScript); +loadScriptPromise(...).then(...); +``` + +コードは多少複雑に見えますが、`loadScript` 関数の Promise 化をしており、本質的には上で書いたものと同じです。 + +`promisify(f)` の呼び出しは、`f` `(*)` のラッパーを返します。ラッパーは promise を返し、呼び出しを元の `f` に転送し、カスタムコールバック `(**)` で結果を追跡します。 + +ここで、`promisify` は、元の関数は2つの引数 `(err, result)` を持つコールバックを期待している前提です。これはもっともよく出くわすパターンです。そして、カスタムコールバックはまさに正しい形式であり、`promisify` はこのようなケースで上手く機能します。 + +しかし、仮に元の `f` がより多くの引数 `callback(err, res1, res2)` を期待しているとしたらどうなるでしょうか? + +複数のコールバックの結果の配列を返す `promifify` の修正です: + +- `promisify(f)` が呼ばれた場合は、上と同様に動作します。 +- `promisify(f, true)` が呼ばれた場合は、コールバックの結果配列で resolve する promise を返します。これが複数の引数をもつコールバックに対するものです。 + +```js +// 結果の配列を得る場合は promisify(f, true) +function promisify(f, manyArgs = false) { + return function (...args) { + return new Promise((resolve, reject) => { + function *!*callback(err, ...results*/!*) { // f のカスタムコールバック + if (err) { + return reject(err); + } else { + // manyArgs が指定されている場合、すべてのコールバック結果で resolve します + *!*resolve(manyArgs ? results : results[0]);*/!* + } + } + + args.push(callback); + + f.call(this, ...args); + }); + }; +}; + +// 使用例: +f = promisify(f, true); +f(...).then(arrayOfResults => ..., err => ...) +``` + +ご覧の通り、基本的には上記と同様ですが、`resolve` は `manyArgs` が true がどうかによって、1つまたはすべての引数で呼び出されます。 + +ケースによっては、`err` はまったくないかもしれません: `callback(result)`, また、コールバックの形式が珍しいような場合には、ヘルパーを使わず、手動でこのような関数たちを Promise 化するのがよいでしょう。 + +もう少し柔軟な Promisification 関数を持つモジュールもあります。例えば、[es6-promisify](https://github.com/digitaldesignlabs/es6-promisify) です。Node.js では、組み込みの `util.promisify` があります。 + +```smart +Promisification は素晴らしいアプローチです。特に `async/await` (次の章で説明します)を使うときには。ですが、これはコールバックの完全な置き換えにはなりません。 + +覚えておいてください、Promise は1つの結果のみを持ちますが、コールバックは技術的には何度も呼ぶことができます。 + +そのため、Promisification はコールバックを1度だけ呼ぶ関数のみを対象としています。それ以降の呼び出しは無視されます。 +``` diff --git a/1-js/11-async/07-microtask-queue/article.md b/1-js/11-async/07-microtask-queue/article.md new file mode 100644 index 0000000000..1459076975 --- /dev/null +++ b/1-js/11-async/07-microtask-queue/article.md @@ -0,0 +1,112 @@ + +# Microtasks + +Promise ハンドラ `.then`/`.catch`/`.finally` は常に非同期です。 + +たとえ Promise がすくに解決されたとしても、`.then`/`.catch`/`.finally` の *下* にあるコードはこれらのハンドラの前に実行されます。 + +デモです: + +```js run +let promise = Promise.resolve(); + +promise.then(() => alert("promise done!")); + +alert("code finished"); // このアラートが最初に表示されます +``` + +実行すると、`code finished` が最初に現れ、その後 `promise done!` が表示されます。 + +Promise は最初から確実に終わっているので、これは奇妙です。 + +なぜ `.then` が後でトリガーされたのでしょう?何がおきているのでしょう? + +## Microtasks キュー + +非同期タスクには適切な管理が必要です。そのために、ECMA 標準では内部キュー `PromiseJobs` について述べています。これは "microtask キュー"(v8 用語)と呼ばれることが多いです。 + +[スペック](https://tc39.github.io/ecma262/#sec-jobs-and-job-queues)では次のように述べられています: + +- キューは先入れ先出し(first-in-first-out)です: 先にキューに入れられたタスクが最初に実行されます。 +- タスクの実行は他になにも実行されていないときにだけ開始されます。 + +簡単に言うと、Promise が準備できると、その `.then/catch/finally` ハンドラはキューに入れられます。それらはまだ実行されていません。JavaScriptエンジンは、現在のコードがないときにキューからタスクを取り、実行します。 + +なので、上の例では先に "code finished" が表示されました。 + +![](promiseQueue.svg) + +Promise ハンドラは常に内部キューを通ります。 + +複数の `.then/catch/finally` のチェーンがある場合、それらはすべて非同期に実行されます。つまり、最初にキューに入れられ、現在のコードが完了し、前にキューに入れられたハンドラが終了したときに実行されます。 + +**順序が重要な場合はどうなるでしょうか? `promise done` の後に `code finished` を処理したい場合はどうすればよいでしょう?** + +簡単です、単に `.then` でキューに入れるだけです: + +```js run +Promise.resolve() + .then(() => alert("promise done!")) + .then(() => alert("code finished")); +``` + +これで順番は期待通りです。 + +## 未処理の拒否(Unhandled rejection) + + の章の `unhandledrejection` イベントを覚えていますか? + +今や、我々は JavaScript が未処理の拒否があったことをどのように見つけるのかを正確に知ることができます。 + +**"未処理の拒否" は、microtask キューの最後で Promise エラーが処理されない場合に発生します。** + +通常、エラーが予想される場合には、それを処理するために Promise チェーンに `.catch` を追加します: + +```js run +let promise = Promise.reject(new Error("Promise Failed!")); +*!* +promise.catch(err => alert('caught')); +*/!* + +// 実行されません: error handled +window.addEventListener('unhandledrejection', event => alert(event.reason)); +``` + +...ですが、`.catch` を忘れていた場合、microtask キューが空になった後、エンジンはイベントをトリガーします: + +```js run +let promise = Promise.reject(new Error("Promise Failed!")); + +// 実行されます: Promise Failed! +window.addEventListener('unhandledrejection', event => alert(event.reason)); +``` + +仮に、次のように後でエラーを処理するとどうなるでしょう?: + +```js run +let promise = Promise.reject(new Error("Promise Failed!")); +*!* +setTimeout(() => promise.catch(err => alert('caught')), 1000); +*/!* + +// 実行されます: Error: Promise Failed! +window.addEventListener('unhandledrejection', event => alert(event.reason)); +``` + +実行すると、最初に `Promise Failed!` が表示され、その後 `caught` が表示されます。 + +microtask キューについて知らなければ、不思議に思うでしょう: "なぜ `unhandledrejection` ハンドラが実行されるのでしょう? エラーをキャッチしているのに!" + +しかし、今は `unhandledrejection` は microtask キューが完了したときに生成されることが分かりました。: エンジンは Promise を検査し、いずれかが "拒否(rejected)" 状態であれば、イベントをトリガーします。 + +上記の例では、`setTimeout` により追加された `.catch` もトリガーされますが、`unhandledrejection` はすでに発生した後なので、何も変わりません。 + +## サマリ + +すべての Promise アクションは、"microtask キュー"(v8 用語)とも呼ばれる、内部の "promise jobs" キューを通るため、Promise の処理は常に非同期です。 + +そのため、`.then/catch/finally` ハンドラは常に現在のコードが終了した後に呼ばれます。 + +あるコードが `.then/catch/finally` の後に実行されることを保証する必要がある場合、Promise チェーンを使い `.then` で呼び出します + +ブラウザも Node.js も含めほとんどの JavaScript エンジンでは、microtask の概念は "イベントループ" と "microtask" と密接に関連しています。これらは Promise とは直接は関係ないので、チュートリアルの別のパート、チャプター で説明しています。 diff --git a/1-js/11-async/07-microtask-queue/promiseQueue.svg b/1-js/11-async/07-microtask-queue/promiseQueue.svg new file mode 100644 index 0000000000..c802c44a01 --- /dev/null +++ b/1-js/11-async/07-microtask-queue/promiseQueue.svg @@ -0,0 +1 @@ +promise . then ( handler ); ... alert ( "code finished" );handler enqueuedqueued handler runsscript execution finished \ No newline at end of file diff --git a/1-js/11-async/08-async-await/01-rewrite-async/solution.md b/1-js/11-async/08-async-await/01-rewrite-async/solution.md new file mode 100644 index 0000000000..4daab2935c --- /dev/null +++ b/1-js/11-async/08-async-await/01-rewrite-async/solution.md @@ -0,0 +1,33 @@ + +補足はコードの下にあります: + +```js run +async function loadJson(url) { // (1) + let response = await fetch(url); // (2) + + if (response.status == 200) { + let json = await response.json(); // (3) + return json; + } + + throw new Error(response.status); +} + +loadJson('no-such-user.json') + .catch(alert); // Error: 404 (4) +``` + +補足: + +1. 関数 `loadJson` は `async` になります。 +2. すべての内側の `.then` は `await` に置き換えられます。 +3. 次のように、await するのではなく、`response.json()` を返すこともできます。: + + ```js + if (response.status == 200) { + return response.json(); // (3) + } + ``` + + そうすると、外側のコードはその promise を解決するために `await` する必要があります。 +4. `loadJson` からスローされたエラーは `.catch` で処理されます。そこでは `await loadJson(…)` を使うことができません。なぜなら `async` 関数の中ではないからです。 diff --git a/6-async/05-async-await/01-rewrite-async/task.md b/1-js/11-async/08-async-await/01-rewrite-async/task.md similarity index 100% rename from 6-async/05-async-await/01-rewrite-async/task.md rename to 1-js/11-async/08-async-await/01-rewrite-async/task.md diff --git a/6-async/05-async-await/01-rewrite-async-2/solution.md b/1-js/11-async/08-async-await/02-rewrite-async-2/solution.md similarity index 100% rename from 6-async/05-async-await/01-rewrite-async-2/solution.md rename to 1-js/11-async/08-async-await/02-rewrite-async-2/solution.md diff --git a/6-async/05-async-await/01-rewrite-async-2/task.md b/1-js/11-async/08-async-await/02-rewrite-async-2/task.md similarity index 100% rename from 6-async/05-async-await/01-rewrite-async-2/task.md rename to 1-js/11-async/08-async-await/02-rewrite-async-2/task.md diff --git a/1-js/11-async/08-async-await/03-async-from-regular/solution.md b/1-js/11-async/08-async-await/03-async-from-regular/solution.md new file mode 100644 index 0000000000..f551356ca6 --- /dev/null +++ b/1-js/11-async/08-async-await/03-async-from-regular/solution.md @@ -0,0 +1,20 @@ + + +`async` 呼び出しを Promise として扱い、それに `.then` をつけるだけです。 + +```js run +async function wait() { + await new Promise(resolve => setTimeout(resolve, 1000)); + + return 10; +} + +function f() { + // 1秒後に 10を表示 +*!* + wait().then(result => alert(result)); +*/!* +} + +f(); +``` diff --git a/1-js/11-async/08-async-await/03-async-from-regular/task.md b/1-js/11-async/08-async-await/03-async-from-regular/task.md new file mode 100644 index 0000000000..8991fc723c --- /dev/null +++ b/1-js/11-async/08-async-await/03-async-from-regular/task.md @@ -0,0 +1,20 @@ + +# 非 async から async を呼び出す + +"通常" の関数があります。そこから `async` 呼び出しを行い、その結果を使うにはどうすればよいでしょう? + +```js +async function wait() { + await new Promise(resolve => setTimeout(resolve, 1000)); + + return 10; +} + +function f() { + // ...ここに何を書きますか? + // async wait() をして 10 を取得するのを待ちます + // 覚えておいてください、"await" は使えません +} +``` + +P.S. このタスクは技術的には非常に単純です。が、async/await に不慣れた開発者によくある質問です。 diff --git a/1-js/11-async/08-async-await/article.md b/1-js/11-async/08-async-await/article.md new file mode 100644 index 0000000000..4c50228666 --- /dev/null +++ b/1-js/11-async/08-async-await/article.md @@ -0,0 +1,311 @@ +# Async/await + +"async/await" と呼ばれる、より快適に promise を利用する特別な構文があります。驚くほど簡単に理解し、使用することができます。 + +## Async 関数 + +`async` キーワードから始めましょう。次のように関数の前に置くことができます: + +```js +async function f() { + return 1; +} +``` + +関数の前の用語 "async" は1つの単純なことを意味します: 関数は常に promise を返します。コード中に `return <非 promise>` がある場合、JavaScript は自動的にその値を持つ 解決された promise にラップします。 + +例えば、上のコードは 結果 `1` を持つ解決された promise を返します。テストしてみましょう: t it: + +```js run +async function f() { + return 1; +} + +f().then(alert); // 1 +``` + +...明示的に promise を返すこともでき、それは同じです: + +```js run +async function f() { + return Promise.resolve(1); +} + +f().then(alert); // 1 +``` + +したがって、`async` は関数が promise を返すことを保証し、非promise をその中にラップします。シンプルですよね?しかし、それだけではありません。`async` 関数の中でのみ動作する別のキーワード `await` があります。これはとてもクールです。 + +## Await + +構文: + +```js +// async 関数の中でのみ動作します +let value = await promise; +``` + +キーワード `await` は promise が確定しその結果を返すまで、JavaScript を待機させます。 ult. + +これは1秒で解決する promise の例です: +```js run +async function f() { + + let promise = new Promise((resolve, reject) => { + setTimeout(() => resolve("done!"), 1000) + }); + +*!* + let result = await promise; // promise が解決するまで待ちます (*) +*/!* + + alert(result); // "done!" +} + +f(); +``` + +関数の実行は行 `(*)` で "一時停止" し、promise が確定したときに再開し、`result` がその結果になります。 そのため、上のコードは1秒後に "done!" を表示します。 + +`await` は文字通り promise が確定するまで JavaScript を待ってから、その結果で続くことに注目しましょう。その間、エンジンは他のジョブ(他のスクリプトを実行し、イベントを処理するなど)を実行することができるため、CPUリソースを必要としません。 + +これは、`promise.then` よりも promise の結果を得るためのより洗練された構文です。読みやすく、書くのが簡単です。 + +````warn header="通常の関数で `await` を使うことはできません" +非async関数で `await` を使おうとした場合、構文エラーになります。: + +```js run +function f() { + let promise = Promise.resolve(1); +*!* + let result = await promise; // Syntax error +*/!* +} +``` + +関数の前に `async` を置き忘れた場合にこのエラーが発生します。先程言ったように、`await` は `async function` の中でのみ動作します。 +```` + +チャプター から例 `showAvatar()` を取り、`async/await` を使って書き直してみましょう: + +1. `.then` 呼び出しを `await` に置き換える必要があります。 +2. また、機能させるために関数を `async` にする必要があります。 + +```js run +async function showAvatar() { + + // JSON を読み込む + let response = await fetch('/article/promise-chaining/user.json'); + let user = await response.json(); + + // github ユーザを読み込む + let githubResponse = await fetch(`https://api.github.com/users/${user.name}`); + let githubUser = await githubResponse.json(); + + // アバターを表示する + let img = document.createElement('img'); + img.src = githubUser.avatar_url; + img.className = "promise-avatar-example"; + document.body.append(img); + + // 3秒待つ + await new Promise((resolve, reject) => setTimeout(resolve, 3000)); + + img.remove(); + + return githubUser; +} + +showAvatar(); +``` + +非常にスッキリし、読みやすいですよね?前よりもはるかに良いです。 + +````smart header="`await` はトップレベルのコードでは動作しません" +最近のブラウザは、モジュールの内側ではトップレベルの `await` は問題なく動作します。モジュールに関しては の記事で説明します。 + +例: + +```js run module +// モジュールのトップレベルでコードが実行される想定です +let response = await fetch('/article/promise-chaining/user.json'); +let user = await response.json(); + +console.log(user); +``` + +モジュール未使用、あるいは[古いブラウザ](https://caniuse.com/mdn-javascript_operators_await_top_level)でサポートが必要な場合、無名の async 関数でラップする方法があります。 + +次のようになります: + +```js +(async () => { + let response = await fetch('/article/promise-chaining/user.json'); + let user = await response.json(); + ... +})(); +``` + +```` + +````smart header="`await` は thenable を許容します" +`promise.then` のように、`await` は thenable オブジェクト(`then` メソッドを呼ぶことができるもの)を使うことができます。繰り返しになりますが、このアイデアは、サードパーティオブジェクトは promise ではなく、promise 互換である場合があるということです(`.then` をサポートしている場合、`await` で使えます)。 + +例えば、ここで `await` は `new Thenable(1)` を許容します: + +```js run +class Thenable { + constructor(num) { + this.num = num; + } + then(resolve, reject) { + alert(resolve); // function() { native code } + // 1000ms 後に this.num*2 で解決する + setTimeout(() => resolve(this.num * 2), 1000); // (*) + } +}; + +async function f() { + // 1秒待って、結果は 2 になる + let result = await new Thenable(1); + alert(result); +} + +f(); +``` + +もし `await` が `.then` で非promise オブジェクトを得た場合、引数としてネイティブ関数 `resolve`, `reject` を提供しているメソッドを呼び出します。次に `await` はいずれかが呼ばれるまで待ち(上の例では、行 `(*)` です)、その結果で進みます。 +```` + +````smart header="Async メソッド" +クラスメソッドもまた async になれます。ただ前に `async` を置くだけです。 + +```js run +class Waiter { +*!* + async wait() { +*/!* + return await Promise.resolve(1); + } +} + +new Waiter() + .wait() + .then(alert); // 1 +``` +意味は同じです: 返却される値が promise であることを保証し、`await` を有効にします。 + +```` +## エラー処理 + +もし promise が正常に解決すると、`await promise` は結果を返します。しかし拒否(reject) の場合はエラーをスローします。それはちょうどその行に `throw` 文があるように振る舞います。 + +このコードは: + +```js +async function f() { +*!* + await Promise.reject(new Error("Whoops!")); +*/!* +} +``` + +...これと同じです: + +```js +async function f() { +*!* + throw new Error("Whoops!"); +*/!* +} +``` + +実際には、promise を拒否するまでに時間がかかる場合があります。なので、`await` は待ち、その後エラーをスローします。 + +エラーは `try..catch` でキャッチすることができ、それは通常の `throw` と同じ方法です: + +```js run +async function f() { + + try { + let response = await fetch('http://no-such-url'); + } catch(err) { +*!* + alert(err); // TypeError: failed to fetch +*/!* + } +} + +f(); +``` + +エラーの場合、コントロールは `catch` ブロックにジャンプします。複数行をラップすることも可能です: + +```js run +async function f() { + + try { + let response = await fetch('/no-user-here'); + let user = await response.json(); + } catch(err) { + // fetch と response.json 両方のエラーをキャッチ + alert(err); + } +} + +f(); +``` + +もし `try..catch` がない場合、async 関数 `f()` の呼び出しによって生成された promise は拒否されます。それを処理にするには `.catch` を追加します。: + +```js run +async function f() { + let response = await fetch('http://no-such-url'); +} + +// f() は拒否された promise になる +*!* +f().catch(alert); // TypeError: failed to fetch // (*) +*/!* +``` + +そこに `.catch` を追加し忘れると、未処理の promise エラーを得ます(それはコンソールで見えます)。チャプター で説明した通り、グローバルイベントハンドラを使用することでこのようなエラーをキャッチすることができます。 + + +```smart header="`async/await` と `promise.then/catch`" +`async/await` を使用するとき、`.then` はほとんど必要がありません。なぜなら `await` は私たちを待っているからです。そして `.catch` の代わりに通常の `try..catch` を使うことができます。それは通常(常にではないですが)より便利です。 + +しかし、コードの最上位のレベルでは、`async` 関数の外にいるときは構文的に `await` を使うことができないため、最終的な結果または落ちるようなエラーを処理するために `.then/catch` を追加するのが普通です。上の例の行 `(*)` のように。 +``` + +````smart header="`async/await` は `Promise.all` とうまく動作します" +複数の promise を待つ必要があるとき、`Promise.all` でラップしてから `await` できます。 + +```js +// 結果の配列をまつ +let results = await Promise.all([ + fetch(url1), + fetch(url2), + ... +]); +``` + +エラーが発生した場合、それは通常通り伝搬します: 失敗した promise から `Promise.all` に伝播し、呼び出しのまわりで `try..catch` を使ってキャッチができる例外になります。 + +```` + +## サマリ + +関数の前の `async` キーワードは2つの効果があります: + +1. 常に promise を返します +2. その中で `await` を使えるようにします + +promise の前の `await` キーワードは、 promise が確定するまで JavaScript を待たせ、次のことをします: + +1. それがエラーであれば、まさにその場所で `throw error` が呼び出されたのと同じ例外が生成されます。 +2. それ以外の場合は、結果を返すので、値に割り当てることができます。 + +共に、読み書きするのが簡単な非同期コードを書くことができる素晴らしいフレームワークを提供します。 + +`async/await` と一緒に `promise.then/catch` を書く必要はほとんどありませんが、時には(例えば最も外側のスコープで)これらのメソッドを使わなければならないことがあるので、これらが promise に基づいていることを忘れてはいけません。 また、`Promise.all` は同時に多くのタスクを待つ良い方法です。 diff --git a/6-async/05-async-await/head.html b/1-js/11-async/08-async-await/head.html similarity index 100% rename from 6-async/05-async-await/head.html rename to 1-js/11-async/08-async-await/head.html diff --git a/1-js/11-async/index.md b/1-js/11-async/index.md new file mode 100644 index 0000000000..a626cee4f0 --- /dev/null +++ b/1-js/11-async/index.md @@ -0,0 +1,2 @@ + +# Promise, async/await diff --git a/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/_js.view/solution.js b/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/_js.view/solution.js new file mode 100644 index 0000000000..45c8b071bb --- /dev/null +++ b/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/_js.view/solution.js @@ -0,0 +1,9 @@ +function* pseudoRandom(seed) { + let value = seed; + + while(true) { + value = value * 16807 % 2147483647 + yield value; + } + +}; diff --git a/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/_js.view/test.js b/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/_js.view/test.js new file mode 100644 index 0000000000..5f3a0ae87e --- /dev/null +++ b/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/_js.view/test.js @@ -0,0 +1,21 @@ +describe("pseudoRandom", function() { + + it("follows the formula", function() { + let generator = pseudoRandom(1); + + assert.equal(generator.next().value, 16807); + assert.equal(generator.next().value, 282475249); + assert.equal(generator.next().value, 1622650073); + }); + + + it("returns same value for the same seed", function() { + let generator1 = pseudoRandom(123); + let generator2 = pseudoRandom(123); + + assert.deepEqual(generator1.next(), generator2.next()); + assert.deepEqual(generator1.next(), generator2.next()); + assert.deepEqual(generator1.next(), generator2.next()); + }); + +}); diff --git a/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/solution.md b/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/solution.md new file mode 100644 index 0000000000..0497128889 --- /dev/null +++ b/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/solution.md @@ -0,0 +1,38 @@ +```js run demo +function* pseudoRandom(seed) { + let value = seed; + + while(true) { + value = value * 16807 % 2147483647 + yield value; + } + +}; + +let generator = pseudoRandom(1); + +alert(generator.next().value); // 16807 +alert(generator.next().value); // 282475249 +alert(generator.next().value); // 1622650073 +``` + +注意してください。次のように通常の関数でも同じことができます: + +```js run +function pseudoRandom(seed) { + let value = seed; + + return function() { + value = value * 16807 % 2147483647; + return value; + } +} + +let generator = pseudoRandom(1); + +alert(generator()); // 16807 +alert(generator()); // 282475249 +alert(generator()); // 1622650073 +``` + +これは、このコンテキストでは問題ありません。しかし、どこかで役立つかのしれない `for..of` を使ったイテレートや、ジェネレータの合成を使うことはできなくなります。 diff --git a/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/task.md b/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/task.md new file mode 100644 index 0000000000..17d390a2f6 --- /dev/null +++ b/1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/task.md @@ -0,0 +1,34 @@ + +# 疑似乱数ジェネレータ + +ランダムデータが必要となる多くの場面があります。 + +その1つはテストです。テキスト、数値など、上手くテストを行うためにはランダムなデータが必要になります。 + +JavaScript では `Math.random()` を使うことができます。しかし、何か問題が起きた場合、まったく同じデータを使用して繰り返しテストができればよいです。 + +そのために、"シード疑似乱数ジェネレータ" と呼ばれるものが使われます。それらは最初の値である "シード(種)" を取り、以降公式を使って次の値を生成します。同じシードは同じ一覧の数列を生成するので、フロー全体を簡単に再現することができます。繰り返すのに覚えておく必要があるのはシードだけです。 + +これは、このような公式の例で、幾分か一様に分布した値を生成します。: + +``` +next = previous * 16807 % 2147483647 +``` + +シードに `1` を使うと、値は次のようになります: +1. `16807` +2. `282475249` +3. `1622650073` +4. ...など... + +このタスクは、`seed` を取り、この式でジェネレータを生成するジェネレータ関数 `pseudoRandom(seed)` を作成することです。 + +使用例: + +```js +let generator = pseudoRandom(1); + +alert(generator.next().value); // 16807 +alert(generator.next().value); // 282475249 +alert(generator.next().value); // 1622650073 +``` diff --git a/1-js/12-generators-iterators/1-generators/article.md b/1-js/12-generators-iterators/1-generators/article.md new file mode 100644 index 0000000000..b1907aa307 --- /dev/null +++ b/1-js/12-generators-iterators/1-generators/article.md @@ -0,0 +1,469 @@ + +# ジェネレータ + +通常の関数は、単一の値だけを返します(もしくはなにも返しません)。 + +ジェネレータは、要求に応じて次々に複数の値、場合によっては無限の数の値を返す("生み出す")ことができます。それらは [反復可能(iterables)](info:iterable) と上手く機能し、データストリームを簡単に作成することができます。 + +## ジェネレータ関数 + +ジェネレータを作成するには、特別な構文構造: `function*`、いわゆる "ジェネレータ関数" を使用する必要があります。 + +このようになります: + +```js +function* generateSequence() { + yield 1; + yield 2; + return 3; +} +``` + +`generateSequence()` が呼ばれたとき、コードは実行されません。代わりに、"ジェネレータ" と呼ばれる特別なオブジェクトを返します。 + +```js +// "ジェネレータ関数" は "ジェネレータオブジェクト" を生成します。 +let generator = generateSequence(); +``` + +`generator` オブジェクトは "凍結された関数呼び出し" と捉えることができます。: + +![](generateSequence-1.svg) + +作成時に、コードの実行は最初の部分で一時停止されます。 + +ジェネレータのメインのメソッドは `next()` です。呼ばれると、最も近い `yield ` 文まで実行を再開します。その後、実行は一時停止し、値は外部のコードに返却されます。 + +例えば、ここではジェネレータを作成し、最初に戻される値を取得しています: + +```js run +function* generateSequence() { + yield 1; + yield 2; + return 3; +} + +let generator = generateSequence(); + +*!* +let one = generator.next(); +*/!* + +alert(JSON.stringify(one)); // {value: 1, done: false} +``` + +`next()` の結果は常にオブジェクトです: +- `value`: 戻された値 +- `done`: コードがまだ終わっていない場合は `false`, そうでなければ `true`. + +現時点では、最初の値だけ取得しました: + +![](generateSequence-2.svg) + +再び `generator.next()` を呼びましょう。実行が再開し、次の `yield` を貸します。: + +```js +let two = generator.next(); + +alert(JSON.stringify(two)); // {value: 2, done: false} +``` + +![](generateSequence-3.svg) + +そして、3回目を呼び出すと、実行は関数を終了する `return` 文に到達します。 + +```js +let three = generator.next(); + +alert(JSON.stringify(three)); // {value: 3, *!*done: true*/!*} +``` + +![](generateSequence-4.svg) + +これでジェネレータが済みました。`done:true` でそれが判断でき、`value:3` を最終結果として処理します。 + +新たな `generator.next()` 呼び出しはこれ以上意味をなしません。それを行っても、同じオブジェクト `{done: true}` が返却されます。 + +ジェネレータを "ロールバック" する方法はありません。しかし、`generateSequence()` 呼び出しによって、別の物を作ることはできます。 + +これまでのところ、理解すべき最も重要なことは、ジェネレータ関数は通常の関数とは異なり、コードを実行しないことです。それらは "ジェネレータ工場(ファクトリー)" として機能します。 `function*` の実行はジェネレータを返し、その後、ジェネレータに値を要求します。 + +```smart header="`function* f(…)` それとも `function *f(…)`?" +これは軽い宗教的な質問で、両方の構文は正しいです。 + +しかし、アスタリスク `*`はジェネレータ関数であることを表し、名前ではなく種類を表すので、通常は最初の構文がより好まれます。したがって、`function` キーワードに付けてください。 +``` + +## ジェネレータは反復可能です + +おそらく `next()` メソッドを見て既に推測していると思いますが、ジェネレータは [反復可能(iterable)](info:iterable)です。 + +`for..of` によって、値をループすることができます: + +```js run +function* generateSequence() { + yield 1; + yield 2; + return 3; +} + +let generator = generateSequence(); + +for(let value of generator) { + alert(value); // 1, 次に 2 +} +``` + +これは `.next().value` を呼び出すよりも、ジェネレータを操作するのにはるかに見栄えの良い方法ですね。 + +...しかし、注意してください: 上の例では `1` が表示された後 `2` が表示され、それですべてです。`3` は表示されません! + +これは、`done: true` のとき、for-of イテレーションは最後の `value` を無視するからです。なので、すべての結果を `for..of` で表示したい場合は、それらを `yield` で返さなければなりません: + +```js run +function* generateSequence() { + yield 1; + yield 2; +*!* + yield 3; +*/!* +} + +let generator = generateSequence(); + +for(let value of generator) { + alert(value); // 1, 次に 2, 次に 3 +} +``` + +当然、ジェネレータは反復可能なので、スプレッド演算子`...` のような、関連するすべての機能を呼び出すことができます。: + +```js run +function* generateSequence() { + yield 1; + yield 2; + yield 3; +} + +let sequence = [0, ...generateSequence()]; + +alert(sequence); // 0, 1, 2, 3 +``` + +上のコードでは、`...generateSequence()` は iterable をアイテムの配列に変換します(スプレッド演算子についてはチャプター [](info:rest-parameters-spread-operator#spread-operator)) を読んでください)。 + +## 反復可能(iterable)の代わりにジェネレータを使用する + +少し前、チャプター [](info:iterable) で、値 `from..to` を返す反復可能な `range` オブジェクトを作りました。 + +ここで、そのコードを思い出しましょう。: + +```js run +let range = { + from: 1, + to: 5, + + // for..of は最初にこのメソッドを一度呼び出します + [Symbol.iterator]() { + // ...これは iterator オブジェクトを返します: + // 以降, for..of はそのオブジェクトでのみ機能し、次の値を要求します。 + return { + current: this.from, + last: this.to, + + // next() は for..of ループの各イテレーションで呼ばれます + next() { + // 値をオブジェクトとして返す必要があります {done:.., value :...} + if (this.current <= this.last) { + return { done: false, value: this.current++ }; + } else { + return { done: true }; + } + } + }; + } +}; + +alert([...range]); // 1,2,3,4,5 +``` + +ジェネレータを使用して反復可能(iterable)のシーケンスを作るほうが、遥かにエレガントです: + +```js run +function* generateSequence(start, end) { + for (let i = start; i <= end; i++) { + yield i; + } +} + +let sequence = [...generateSequence(1,5)]; + +alert(sequence); // 1, 2, 3, 4, 5 +``` + +...ですが、仮にカスタムの `range` オブジェクトを保持したいとしたらどうなるでしょうか? + +## Symbol.iterator からジェネレータへの変換 + +ジェネレータを `Symbol.iterator` として提供することで、両方の世界からベストを得ることができます: + +```js run +let range = { + from: 1, + to: 5, + + *[Symbol.iterator]() { // [Symbol.iterator]: function*() の短縮記法 + for(let value = this.from; value <= this.to; value++) { + yield value; + } + } +}; + +alert( [...range] ); // 1,2,3,4,5 +``` + +`range` オブジェクトはいま反復可能です。 + +これは非常に上手く機能します。なぜなら、`range[Symbol.iterator]` が呼ばれたとき、: +- オブジェクトを返します(いまはジェネレータです)。 +- そのオブジェクトは `.next()` メソッドを持っています(そう、ジェネレータはそれを持っています)。 +- これは、`{value: ..., done: true/false}` の形式で値を返します。 + +もちろん、これは偶然ではありません。ジェネレータは iterable をより簡単にすることを目的としているので、そのようにすることができます。 + +ジェネレータを使用した最後のパターンは、元の iterable なコードよりも遥かに簡潔で、かつ同じ機能を提供します。 + +```smart header="ジェネレータは永遠に続く可能性があります" +上の例では、有限の数字列を生成しましたが、永遠に値を生成するジェネレータを作ることも可能です。例えば、終わりのない疑似乱数の列です。 + +それは必ず `for..of` の中で `break` を必要とします。そうでなければ、ループは永遠に繰り返され、ハングします。 +``` + +## ジェネレータの合成 + +ジェネレータの合成は、ジェネレータ同士を透過的に "埋め込む" ことを可能にするジェネレータの特別な機能です。 +例えば、次のシーケンスを生成したいとします。 + +- 数字 `0..9` (文字コード 48..57) +- 大文字の `A..Z` が続く(文字コード 65..90) +- アルファベット文字 `a..z` が続く(文字コード 97..122) + +次に、そこから選択した文字でパスワードを作成することを考えます(同様にして構文文字を追加することも可能です)が、最初にシーケンスを生成する必要があります。 + +すでに `function* generateSequence(start, end)` があるので、3つのシーケンスを順に作りだすのに、これを再利用しましょう。 + +通常の関数では、複数の別々の関数からの結果をまとめるためには、それらを呼び出し、結果を格納し、最後に結合します。 + +ジェネレータの場合は、次のようによりスマートに行うことができます: + +```js run +function* generateSequence(start, end) { + for (let i = start; i <= end; i++) yield i; +} + +function* generatePasswordCodes() { + +*!* + // 0..9 + yield* generateSequence(48, 57); + + // A..Z + yield* generateSequence(65, 90); + + // a..z + yield* generateSequence(97, 122); +*/!* + +} + +let str = ''; + +for(let code of generatePasswordCodes()) { + str += String.fromCharCode(code); +} + +alert(str); // 0..9A..Za..z +``` + +この例にある特別な `yield*` ディレクティブは合成を担当します。これは、別のジェネレータに実行を *委譲(デリゲート)* します。あるいは、単純に言うと、ジェネレータを実行し、あたかもそれらが呼び出し元のジェネレータ自体によって行われたかのように、それらの yield を外部に透過的に転送します。 + +結果は、入れ子のジェネレータのコードがインライン展開された場合と同じです。: + +```js run +function* generateSequence(start, end) { + for (let i = start; i <= end; i++) yield i; +} + +function* generateAlphaNum() { + +*!* + // yield* generateSequence(48, 57); + for (let i = 48; i <= 57; i++) yield i; + + // yield* generateSequence(65, 90); + for (let i = 65; i <= 90; i++) yield i; + + // yield* generateSequence(97, 122); + for (let i = 97; i <= 122; i++) yield i; +*/!* + +} + +let str = ''; + +for(let code of generateAlphaNum()) { + str += String.fromCharCode(code); +} + +alert(str); // 0..9A..Za..z +``` + +ジェネレータの合成は、あるジェネレータのフローを別のジェネレータに挿入するための、自然な方法です。 + +入れ子のジェネレータからの値のフローが無限の場合でも動作します。それはシンプルで中間結果を格納するための余分なメモリを必要としません。 + +## "yield" は双方向 + +ここまで、ジェネレータは "強化されたイテレータ" のようでした。これはよく利用される方法です。 + +しかし、実際にはジェネレータはより強力で柔軟です。 + +なぜなら、`yield` は双方向だからです: 結果を外部に返すだけでなく、ジェネレータ内部に値を渡す事もできます。 + +そうするためには、引数を持つ `generator.next(arg)` を呼び出す必要があります。この引数は `yield` の結果になります。 + +例を見てみましょう: + +```js run +function* gen() { +*!* + // 質問を外側のコードに渡して答えを待ちます + let result = yield "2 + 2?"; // (*) +*/!* + + alert(result); +} + +let generator = gen(); + +let question = generator.next().value; // <-- yield は値を返します + +generator.next(4); // --> 結果をジェネレータに渡します +``` + +![](genYield2.svg) + +1. 最初の呼び出し `generator.next()` は常に引数なしです。実行を開始し、最初の `yield` ("2+2?") の結果を返します。この時点で、ジェネレータは実行を一時停止します(依然としてその行にいます)。 +2. 次に、上の図にあるように、`yield` の結果は呼び出しコードの `question` 変数に入ります。 +3. `generator.next(4)` でジェネレータが再開し、結果として `4` が入ります: `let result = 4` + +外部のコードはすぐに `next(4)` を呼び出す必要はないことに注目してください。計算に時間がかかる場合があります。これも有効なコードです。: + +```js +// ある時間経過後にジェネレータを再開する +setTimeout(() => generator.next(4), 1000); +``` + +構文は少し変に見えるかもしれません。関数と呼び出しコードがお互いに値を渡し合うことはめったにありません。しかし、それがまさに起こっていることです。 + +より明白にするために、これは別の例です: + +```js run +function* gen() { + let ask1 = yield "2 + 2?"; + + alert(ask1); // 4 + + let ask2 = yield "3 * 3?" + + alert(ask2); // 9 +} + +let generator = gen(); + +alert( generator.next().value ); // "2 + 2?" + +alert( generator.next(4).value ); // "3 * 3?" + +alert( generator.next(9).done ); // true +``` + +実行の図です: + +![](genYield2-2.svg) + +1. まず、`.next()` は実行を開始します。そして最初の `yield` に到達します。 +2. 結果は外部のコードに返却されます。 +3. 2つ目の `.next(4)` は、`4` を最初の `yield` の結果としてジェネレータに戻し、実行を再開します。 +4. ...2つ目の `yield` に到達します。これはジェネレータ呼び出しの結果になります。 +5. 3つ目の `.next(9)` 2つ目の `yield` の結果として `9` をジェネレータに渡し実行を再開します。そして関数の終わりに到達するので、`done: true` です。 + +これは "ピンポン" ゲームのようです。各 `next(value)` (最初のものを除く) はジェネレータに値を渡し、それは現在の `yield` の結果になり、次の `yield` の結果を戻します。 + +## generator.throw + +上の例で見たように、外部のコードは `yield` の結果として値をジェネレータに渡す可能性があります。 + +...しかし、そこでエラーを起こす(スローする)こともできます。エラーも結果の1つなので、これは自然なことです。 + +エラーを `yield` に渡すには、`generator.throw(err)` を呼び出す必要があります。この場合、`err` は `yield` のある行に投げられます。 + +例えば、ここで `"2 + 2?"` の yield はエラーになります: + +```js run +function* gen() { + try { + let result = yield "2 + 2?"; // (1) + + alert("The execution does not reach here, because the exception is thrown above"); + } catch(e) { + alert(e); // エラーを表示します + } +} + +let generator = gen(); + +let question = generator.next().value; + +*!* +generator.throw(new Error("The answer is not found in my database")); // (2) +*/!* +``` + +エラーは、行 `(2)` でジェネレータにスローされ、`yield` のある行 `(1)` で例外となります。上の例では、`try..catch` がそれをキャッチし表示しています。 + +キャッチしない場合、他の例外のように、ジェネレータは呼び出しコードで "落ちます"。 + +呼び出しコードの現在の行は、`(2)` とラベル付けされた `generator.throw` を持つ行です。なので、次のようにここでキャッチすることができます: + +```js run +function* generate() { + let result = yield "2 + 2?"; // この行でエラー +} + +let generator = generate(); + +let question = generator.next().value; + +*!* +try { + generator.throw(new Error("The answer is not found in my database")); +} catch(e) { + alert(e); // エラーを表示します +} +*/!* +``` + +ここでエラーをキャッチしなければ、通常どおり、外部の呼び出しコード(あれば)へ渡され、キャッチされなければスクリプトが強制終了します。 + +## サマリ + +- ジェネレータはジェネレータ関数 `function*(…) {…}` により生成されます。 +- ジェネレータ内部にのみ `yield` 演算子が存在します。 +- 外部のコードとジェネレータは `next/yield` 呼び出しを通して結果をやり取りすることができます。 + +モダンな JavaScript では、ジェネレータはめったに使用されません。しかし、実行中に呼び出しコードとデータをやり取りする関数の機能は非常にユニークであるため、便利なときがあります。 + +また、次のチャプターでは、非同期のジェネレータについて学びます。それは `for` ループで非同期的に生成されたデータをのストリームを読むのに使われます。 + +web プログラミングでは、しばしばストリーミングデータを扱います。e.g. ページングされた結果を取得する必要があるため、これはとても重要なユースケースです。 diff --git a/1-js/12-generators-iterators/1-generators/genYield2-2.svg b/1-js/12-generators-iterators/1-generators/genYield2-2.svg new file mode 100644 index 0000000000..f45e69324d --- /dev/null +++ b/1-js/12-generators-iterators/1-generators/genYield2-2.svg @@ -0,0 +1 @@ +"2 + 2 = ?""3 * 3 = ?". next ( 4 ). next ( 9 )GeneratorCalling code \ No newline at end of file diff --git a/1-js/12-generators-iterators/1-generators/genYield2.svg b/1-js/12-generators-iterators/1-generators/genYield2.svg new file mode 100644 index 0000000000..8d3e257c94 --- /dev/null +++ b/1-js/12-generators-iterators/1-generators/genYield2.svg @@ -0,0 +1 @@ +question = "2 + 2 = ?"GeneratorCalling code.next(4) \ No newline at end of file diff --git a/1-js/12-generators-iterators/1-generators/generateSequence-1.svg b/1-js/12-generators-iterators/1-generators/generateSequence-1.svg new file mode 100644 index 0000000000..138df324cc --- /dev/null +++ b/1-js/12-generators-iterators/1-generators/generateSequence-1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/1-js/12-generators-iterators/1-generators/generateSequence-2.svg b/1-js/12-generators-iterators/1-generators/generateSequence-2.svg new file mode 100644 index 0000000000..4c64e983e1 --- /dev/null +++ b/1-js/12-generators-iterators/1-generators/generateSequence-2.svg @@ -0,0 +1 @@ +{value: 1, done: false} \ No newline at end of file diff --git a/1-js/12-generators-iterators/1-generators/generateSequence-3.svg b/1-js/12-generators-iterators/1-generators/generateSequence-3.svg new file mode 100644 index 0000000000..0af8e9efdc --- /dev/null +++ b/1-js/12-generators-iterators/1-generators/generateSequence-3.svg @@ -0,0 +1 @@ +{value: 2, done: false} \ No newline at end of file diff --git a/1-js/12-generators-iterators/1-generators/generateSequence-4.svg b/1-js/12-generators-iterators/1-generators/generateSequence-4.svg new file mode 100644 index 0000000000..23049fcd21 --- /dev/null +++ b/1-js/12-generators-iterators/1-generators/generateSequence-4.svg @@ -0,0 +1 @@ +{value: 3, done: true} \ No newline at end of file diff --git a/1-js/12-generators-iterators/2-async-iterators-generators/article.md b/1-js/12-generators-iterators/2-async-iterators-generators/article.md new file mode 100644 index 0000000000..404f0803d2 --- /dev/null +++ b/1-js/12-generators-iterators/2-async-iterators-generators/article.md @@ -0,0 +1,417 @@ + +# 非同期イテレーションとジェネレータ + +非同期イテレーションを使用すると、要求に応じて非同期に来るデータに対して反復処理することができます。例えば、ネットワーク経由でチャンクごとに何かをダウンロードする場合です。そして、非同期ジェネレータはそれをさらに便利にします。 + +最初に単純な例を見て構文を把握し、その後、実際のユースケースを見ていきましょう。 + +## 反復可能(iterable)を思い出してください + +反復可能(iterable)についてのトピックを思い出しましょう。 + +ここでは `range` のようなオブジェクトがあると考えます: +```js +let range = { + from: 1, + to: 5 +}; +``` + +そして、これに対して `for(value of range)` のように `for..of` を使用して、`1` から `5` までの値を取得したいとします。 + +つまり、オブジェクトに *反復する機能* を追加したい、です。 + +これは `Symbol.iterator` という名前の特別なメソッドを使用することで実装できます。 + +- このメソッドはループが開始されたときに `for..of` コンストラクトで呼び出され、`next` メソッドをもつオブジェクトを返却する必要があります。 +- 各イテレーションのたびに、`next()` メソッドが実行され、次の値を取得します。 +- `next()` は `{done: true/false, value:}` の形式の値の返却が必要で、`done:true` はループが終わりであることを意味します。 + +以下は、反復可能な `range` の実装です: + +```js run +let range = { + from: 1, + to: 5, + +*!* + [Symbol.iterator]() { // for..of の最初に一度呼ばれます +*/!* + return { + current: this.from, + last: this.to, + +*!* + next() { // 各イテレーションで呼ばれ、次の値を取得します +*/!* + if (this.current <= this.last) { + return { done: false, value: this.current++ }; + } else { + return { done: true }; + } + } + }; + } +}; + +for(let value of range) { + alert(value); // 1 then 2, then 3, then 4, then 5 +} +``` + +不明点があれば、通常のイテレータの詳細について [](info:iterable) を参照してください。 + +## 非同期の反復可能 + +非同期のイテレーションは、非同期(`setTimeout` や他の種類の遅延処理の後)に値がくるときに必要になります。 + +一般的なケースは、オブジェクトが次の値を提供するためにネットワークリクエストを行う必要がある場合です。実際の例については、少し後で説明します。 + +オブジェクトを非同期的に反復可能とするには、: + +1. `Symbol.iterator` の代わりに、`Symbol.asyncIterator` を使用します。 +2. `next()` は promise を返す必要があります(次の値で解決される)。 + - `async` キーワードはこれを処理します。シンプルに `async next()` とできます。 +3. このようなオブジェクトをイテレートするには、`for await (let item of iterable)` ループを使用します。 + - `await` に留意してください。 + +最初の例として、先程と同様、反復可能な `range` オブジェクトを作成しましょう。ですが、今度は1秒毎に値を非同期的に返します。: + +やるべきことは、上のコードに対し少し置き換えるだけです: + +```js run +let range = { + from: 1, + to: 5, + +*!* + [Symbol.asyncIterator]() { // (1) +*/!* + return { + current: this.from, + last: this.to, + +*!* + async next() { // (2) +*/!* + +*!* + // async next の中で、 "await" が使えます + await new Promise(resolve => setTimeout(resolve, 1000)); // (3) +*/!* + + if (this.current <= this.last) { + return { done: false, value: this.current++ }; + } else { + return { done: true }; + } + } + }; + } +}; + +(async () => { + +*!* + for await (let value of range) { // (4) + alert(value); // 1,2,3,4,5 + } +*/!* + +})() +``` + +ご覧の通り、構成は通常のイテレータと同様です: + +1. オブジェクトを非同期的に反復可能にするために、`Symbol.asyncIterator` メソッドが必要です。 `(1)` +2. それは promise を返す `next()` メソッドを持つオブジェクトを返す必要があります。`(2)` +3. `next()` メソッドは `async` である必要はなく、promise を返す通常のメソッドかもしれませんが、`async` は中では `await` が使えるので便利です。ここでは単に1秒だけ遅延させています。`(3)` +4. イテレートするために、`for await(let value of range)` `(4)` を使用しています。すなわち、"for" の後に "await" を追加します。これは `range[Symbol.asyncIterator]()` を一度だけ呼び出し、値に対して `next()` を呼び出します。 + +以下は違いを示した表です: + +| | イテレータ | 非同期イテレータ | +|-------|-----------|-----------------| +| 反復可能を提供するオブジェクトメソッド | `Symbol.iterator` | `Symbol.asyncIterator` | +| `next()` が返す値は | 任意の値 | `Promise` | +| ループするのに使用するものは | `for..of` | `for await..of` | + +````warn header="スプレッド演算子 `...` は非同期には動作しません" +通常の、同期的なイテレータを要する機能は、非同期イテレータでは動作しません。 + +例えば、スプレッド演算子は動作しません: +```js +alert( [...range] ); // Error, no Symbol.iterator +``` + +`Symbol.asyncIterator` ではなく、`Symbol.iterator` があることを期待しているので、これは当然の結果です。 + +また、`for..of` のケースに対しても同様です: `await` なしの構文は `Symbol.iterator` を必要とします。 +```` + +## Recall generators + +ここでジェネレータを思い出しましょう。ジェネレータを使用すると、イテレーションのコードをはるかに短くすることができます。ほとんどの場合、反復可能にしたい場合、ジェネレータを使用します。 + +単純にするために、いくつかの重要な点を省略すると、ジェネレータは "値を生成(yield)する関数" です。この詳細は [](info:generators) で説明しています。 + +ジェネレータは `function*` ( * に注目)でラベル付けされたもので、値を生成すのに `yield` を使用します。ジェネレータをループするのに `for..of` が利用できます。 + +この例は `start` から end` までの一連の値を生成します: + +```js run +function* generateSequence(start, end) { + for (let i = start; i <= end; i++) { + yield i; + } +} + +for(let value of generateSequence(1, 5)) { + alert(value); // 1, then 2, then 3, then 4, then 5 +} +``` + +すでにご存知の通り、オブジェクトを反復可能にするには `Symbol.iterator` の追加が必要です。 + +```js +let range = { + from: 1, + to: 5, +*!* + [Symbol.iterator]() { + return + } +*/!* +} +``` + +`Symbol.iterator` の一般的なプラクティスはジェネレータを返すことで、以下のようにコードをより短くできます: + +```js run +let range = { + from: 1, + to: 5, + + *[Symbol.iterator]() { // [Symbol.iterator]: function*() の短縮形 + for(let value = this.from; value <= this.to; value++) { + yield value; + } + } +}; + +for(let value of range) { + alert(value); // 1, then 2, then 3, then 4, then 5 +} +``` + +より詳細を知りたい場合は、[](info:generators) の章を参照してください。 + +通常のジェネレータでは、`await` は使用できません。すべての値は `for..of` 構造によって同期的である必要があります。 + +仮に、非同期で値を生成したい場合はどうすればよいでしょうか? 例えばネットワークリクエスト。 + +それができるように、非同期ジェネレータじ変更しましょう。 + +## 非同期ジェネレータ + +ほとんどの実践的なアプリケーションでは、一連の値を非同期的に生成するオブジェクトを作成したい場合、非同期ジェネレータが使えます。 + +構文はシンプルです。`function*` の前に `async` をつけます。これでジェネレートが非同期になります。 + +また、次のようにそれをイテレートするのに、`for await (...)` を使用します。 + +```js run +*!*async*/!* function* generateSequence(start, end) { + + for (let i = start; i <= end; i++) { + +*!* + // await が使えます! + await new Promise(resolve => setTimeout(resolve, 1000)); +*/!* + + yield i; + } + +} + +(async () => { + + let generator = generateSequence(1, 5); + for *!*await*/!* (let value of generator) { + alert(value); // 1, then 2, then 3, then 4, then 5 (間に遅延をはさみながら) + } + +})(); +``` + +ジェネレータは非同期なので、その中で `await` や promise に依存するネットワークリクエストを実行したりできます。 + +````smart header="内部的な違い" +技術的には、ジェネレータの詳細を覚えている読者であれば、内部的な違いに気づくかもしれません。 + +非同期ジェネレータの場合、`generator.next()` メソッドは非同期であり、promise を返します。 + +通常のジェネレータでは、`result = generator.next()` を使用して値を取得します。非同期ジェネレータでは、次のように `await` を追加する必要があります: + +```js +result = await generator.next(); // result = {value: ..., done: true/false} +``` +そういうわけで、非同期ジェネレータは `for await...of` で動作します。 +```` + +### Async iterable range + +通常のジェネレータは `Symbol.iterator` を使用することで、イテレーションコードをより短くすることができます。 + +それと同様に、非同期ジェネレータも `Symbol.asyncIterator` を使用することで、非同期のイテレーションを実装することができます。 + +例えば、1秒に1回、非同期に値を生成する `range` オブジェクトを作りたい場合、`Symbol.iterator` を非同期の `Symbol.asyncIterator` に置き換えることで実現できます: + +```js run +let range = { + from: 1, + to: 5, + + // この行は次と同じです: [Symbol.asyncIterator]: async function*() { +*!* + async *[Symbol.asyncIterator]() { +*/!* + for(let value = this.from; value <= this.to; value++) { + + // 値の間に間隔を作り、なにかを待ちます + await new Promise(resolve => setTimeout(resolve, 1000)); + + yield value; + } + } +}; + +(async () => { + + for *!*await*/!* (let value of range) { + alert(value); // 1, then 2, then 3, then 4, then 5 + } + +})(); +``` + +これで、値は繰り返し毎に1秒の遅延で来ます。 + +```smart +技術的には、`Symbol.iterator` と `Symbol.asyncIterator` 両方をオブジェクトに追加することができます。そのため、同期 (`for..of`) と非同期 (`for await..of`) 両方で反復可能です。 + +ただし、実際にはそれは微妙です。 +``` + +## 実例: ページネーション データ + +ここまで基本的な例を見てきました。ここでは実際のユースケースを見ていきましょう。 + +ページング機能を持つデータを提供する多くのオンラインAPIがあります。例えば、ユーザの一覧が必要なとき、ページ毎に取得することができます。: リクエストは事前に定義されたカウント(e.g. 100 ユーザ)のユーザ情報を返し、次のページへの URL を提供します。 + +このパターンは非常に一般的で、ユーザに限ったものではありません。 + +例えば、Github で同じようにページングの形式でコミットを取得することができます。: + +- `https://api.github.com/repos//commits` の形式でURLをリクエストします。 +- 30 コミット分の JSON が返却され、`Link` ヘッダに次のページへのリンクも提供します。 +- その後、そのリンクをより多くのコミットを取得するための次のリクエストとして使用したりすることができます。 + +いま、コミットを取得するより簡単な方法がほしいです。 + +`fetchCommits(repo)` 関数を作り、必要に応じてリクエストを行いコミットを取得します。そして、ページネーションについて考慮します。それは単純な非同期イテレーション `for await..of` です。 + +使い方は以下の通りです: + +```js +for await (let commit of fetchCommits("username/repository")) { + // process commit +} +``` + +これがその関数で、非同期ジェネレータで実装しています: + +```js +async function* fetchCommits(repo) { + let url = `https://api.github.com/repos/${repo}/commits`; + + while (url) { + const response = await fetch(url, { // (1) + headers: {'User-Agent': 'Our script'}, // github は user-agent ヘッダを要求します + }); + + const body = await response.json(); // (2) JSON として response をパース(コミットの配列) + + // (3) ヘッダにある次のページの URL を抽出 + let nextPage = response.headers.get('Link').match(/<(.*?)>; rel="next"/); + nextPage = nextPage && nextPage[1]; + + url = nextPage; + + for(let commit of body) { // (4) ページが終わるまで1つずつ yield commits + yield commit; + } + } +} +``` + +どのように動くかの説明です: + +1. ブラウザの [fetch](info:fetch) メソッドを使ってコミットをダウンロードします。 + + - 最初のURLは `https://api.github.com/repos//commits` で、次のページはレスポンスヘッダの `Link` にあります。 + - `fetch` メソッドでは必要に応じて authorization や他のヘッダ を付与できます。ここでは Github は `User-Agent` を要求します。 +2. コミットは JSON 形式で返却荒れます。 +3. レスポンスの `Link` ヘッダから次のページの URL を取得できます。それは特別なフォーマットなので、そのための正規表現([正規表現](info:regular-expressions)で学びます)を使います。 + - 次のページの URL はこのようになります。: `https://api.github.com/repositories/93253246/commits?page=2`。これは Github 自身により生成されます。 +4. そして、受け取ったすべてのコミットを返し、それらが終了すると、次の `while(url)` イテレーションがトリガーされ、もう1つ要求を行います。 + +使用例 (コンソールにコミット者を表示します): + +```js run +(async () => { + + let count = 0; + + for await (const commit of fetchCommits('javascript-tutorial/en.javascript.info')) { + + console.log(commit.author.login); + + if (++count == 100) { // let's stop at 100 commits + break; + } + } + +})(); + +// 補足: 外部のサンドボックスで実行している場合、上で記述した fetchCommits 関数をここに貼り付ける必要があります。 +``` + +これは私たちが欲しかったものです。 + +内部のページネーションの仕組みは外部からは見えません。我々にとって、それはコミットを返す非同期ジェネレータです。 + +## サマリ + +通常のイテレータとジェネレータは生成に時間のかからないデータと上手く機能します。 + +データが非同期に遅延ありでくることが想定される場合は、それらの非同期に対応したものを使用することができ、`for..of` の代わりに `for await..of` とします。 + +非同期と通常のイテレータの構文の違い: + +| | イテレータ | 非同期イテレータ | +|-------|-----------|-----------------| +| 反復可能を提供するオブジェクトメソッド | `Symbol.iterator` | `Symbol.asyncIterator` | +| `next()` が返す値は | 任意の値 | `Promise` | + +非同期と通常のジェネレータの構文の違い: + +| | ジェネレータ | 非同期ジェネレータ | +|-------|-----------|-----------------| +| 宣言 | `function*` | `async function*` | +| `generator.next()` が返却するもの | `{value:…, done: true/false}` | `{value:…, done: true/false}` に解決する `Promise` | + +Web 開発では、データがチャンクごとに流れるとき、データのストリームを扱うことがよくあります。例えば、大きなファイルのダウンロードやアップロードです。 + +非同期ジェネレータを使用して、このようなデータを処理することもできます。また、ブラウザなどの一部の環境では、Stream と呼ばれる別の API もあることに注目してください。これは、データを変換してあるストリームから別のストリームに渡す特別なインターフェースを提供しますす(e.g ある場所からダウンロードして、すぐに別の場所に送信する場合)。 \ No newline at end of file diff --git a/1-js/12-generators-iterators/2-async-iterators-generators/head.html b/1-js/12-generators-iterators/2-async-iterators-generators/head.html new file mode 100644 index 0000000000..74d66a8b8c --- /dev/null +++ b/1-js/12-generators-iterators/2-async-iterators-generators/head.html @@ -0,0 +1,24 @@ + diff --git a/1-js/12-generators-iterators/index.md b/1-js/12-generators-iterators/index.md new file mode 100644 index 0000000000..9a6bab7c9f --- /dev/null +++ b/1-js/12-generators-iterators/index.md @@ -0,0 +1,2 @@ + +# ジェネレータ, 高度なイテレーション diff --git a/1-js/13-modules/01-modules-intro/article.md b/1-js/13-modules/01-modules-intro/article.md new file mode 100644 index 0000000000..a240dd1227 --- /dev/null +++ b/1-js/13-modules/01-modules-intro/article.md @@ -0,0 +1,418 @@ + +# モジュール, 導入 + +アプリケーションが大きくなるにつれ、それを複数のファイルに分割したくなります。いわゆる 'モジュール' です。通常、モジュールはクラスや便利な関数のライブラリを含みます。 + +長い間、JavaScript には言語レベルのモジュール構文は存在しませんでした。当初はスクリプトが小さくて単純だったため問題ではありませんでした。そのため、モジュールの仕組みも必要ありませんでした。 + +しかし、スクリプトが徐々に複雑になってきたため、コミュニティはコードをモジュールにまとめるための様々な方法を発明しました。 + +いくつか挙げます: + +- [AMD](https://en.wikipedia.org/wiki/Asynchronous_module_definition) -- 最も古いモジュールシステムの1つで、最初はライブラリ[require.js](http://requirejs.org/)で実装されました。 +- [CommonJS](http://wiki.commonjs.org/wiki/Modules/1.1) -- Node.js サーバ用に作られたモジュールシステムです。 +- [UMD](https://github.com/umdjs/umd) -- もう1つのモジュールシステムで、ユニバーサルなものとして提案されています。AMD と CommonJS と互換性があります。 + +今や、これらはゆっくりと歴史の一部になっていますが、依然として古いスクリプトの中で利用されています。 + +言語レベルのモジュールシステムの標準は 2015 年に登場し、それ以来徐々に進化し、今ではすべての主要なブラウザとNode.js でサポートされています。なので、ここからはモダンな JavaScript モジュールについて学んでいきます。 + +## モジュールとは? + +モジュールは単なる1つのファイルです。 + +ディレクティブ `export` と `import` を利用することで、モジュール間で機能を相互にやりとりすることができます。: + +- `export` キーワードは、ファイルの外部からアクセス可能であるべき変数や関数にラベル付けをします。 +- `import` は他のモジュールから機能をインポートできるようにします。 + +例えば、関数をエクスポートしているファイル `sayHi.js` があります: + +```js +// 📁 sayHi.js +export function sayHi(user) { + alert(`Hello, ${user}!`); +} +``` + +...そして、別のファイルでそれをインポートして使います。: + +```js +// 📁 main.js +import {sayHi} from './sayHi.js'; + +alert(sayHi); // function... +sayHi('John'); // Hello, John! +``` + +`import` ディレクティブは現在のファイルからの相対パス `./sayHi.js` のモジュールを読み込み、エクスポートされた関数 `sayHi` を対応する変数に割り当てます。 + +ブラウザで例を実行してみましょう。 + +モジュールは特別なキーワードと機能を提供するので、` +``` + +### モジュールレベルのスコープ + +各モジュールには独自の最上位のスコープがあります。つまり、モジュール内の最上位の変数や関数は他のスクリプトからは見えません。 + +下の例では、2つのスクリプトがインポートされており、`hello.js` は `user.js` で宣言されている変数 `user` を使おうとします。が、別々のモジュールなので失敗します(コンソールでエラーが確認できます): + +[codetabs src="scopes" height="140" current="index.html"] + +モジュールは、外部からアクセス可能にしたいものは `export` を行い、必要なものは `import` が必要です。 + +- `user.js` は `user` 変数のエクスポートが必要です。 +- `hello.js` は `user.js` モジュールからのインポートが必要です。 + +つまり、モジュールでは、グローバル変数に依存するのではなく、インポート/エクスポートを使用します。 + +これは正しい例です: + +[codetabs src="scopes-working" height="140" current="hello.js"] + +ブラウザでは、各 ` + + +``` + +```smart +ブラウザでは、e.g. `window.user = "John"` のように、変数を明示的に `window` プロパティに割り当てることで、ウィンドウレベルのグローバルな変数を作ることができます。 + +以降、`type="module"` の有無に関わらず、すべてのスクリプトはそれが参照できます。 + +とはいえ、このようなグローバル変数の作成は、よく思われない行為です。このようなことは避けるようにしてください。 +``` + +### モジュールコードはインポート時の初回にのみ評価されます + +もし同じモジュールが複数の他の場所でインポートされる場合、そのコードは初回のみ実行されます。その後エクスポートしたものはすべてのインポートしているモジュールで利用されます。 + +1度限りの評価は重要な結果をもたらすため、注意が必要です。 + +いくつか例を見てみましょう。 + +まず、メッセージを表示すると言ったような、副作用をもたらすモジュールコードを実行する場合、複数回インポートしてもトリガされるのは1度だけです(初回)。: + +```js +// 📁 alert.js +alert("Module is evaluated!"); +``` + +```js +// 別のファイルから同じモジュールをインポート + +// 📁 1.js +import `./alert.js`; // Module is evaluated! + +// 📁 2.js +import `./alert.js`; // (nothing) +``` + +モジュールはすでに評価済みなので、2つ目のインポートは何も表示しません。 + +ルールがあります: 初期化やモジュール固有の内部データ構造の作成には、トップレベルのモジュールのコードを使用する必要があります。複数回呼び出し可能にする必要がある場合は、上記の `sayHi` で行ったように、関数としてエクスポートする必要があります。 + +より高度な例を考えてみましょう。 + +モジュールがオブジェクトをエクスポートするとしましょう: + +```js +// 📁 admin.js +export let admin = { + name: "John" +}; +``` + +このモジュールが複数のファイルからインポートされた場合、モジュールは初回にだけ評価され、`admin` オブジェクトが生成され、その後このモジュールをインポートするすべてのモジュールに渡されます。 + +すべてのインポータは正確に1つの `admin` オブジェクトを取得することになります。: + +```js +// 📁 1.js +import {admin} from './admin.js'; +admin.name = "Pete"; + +// 📁 2.js +import {admin} from './admin.js'; +alert(admin.name); // Pete + +*!* +// 1.js と 2.js 同じオブジェクトをインポートしました +// 1.js で行われた変更は 2.js でも見えます +*/!* +``` + +ご覧の通り、`1.js` がインポートした `admin` の `name` プロパティを変更すると、`2.js` は新しい `admin.name` が参照できます。 + +これがまさにモジュールが1度のみ実行されるためです。エクスポートが生成され、インポートする側でそれらを共有するため、何かが `admin` オブジェクトを変更すると、他のインポートしたスクリプトはその変更が見えます。 + +**このような振る舞いは、モジュールを *構成(configure)* できるため実際に非常に便利です。** + +言い換えると、モジュールはセットアップが必要な汎用機能が提供できます。例.認証には資格(credential)が必要です。そして、外部のコードが割り当てることを期待した構成用のオブジェクトをエクスポートします。 + +これは古典的なパターンです: +1. モジュールはいくつかの構成手段をエクスポートします。例.構成オブジェクト +2. 初回インポート時にそれらを初期化し、そのプロパティへ書き込みます。トップレベルのアプリケーションスクリプトがそれを行うかもしれません。 +3. 以降のインポートでは、そのモジュールを使用します。 + +例えば、`admin.js` モジュールは特定の機能(例. 認証など)を提供するかもしれませんが、外部から `admin` オブジェクトにクレデンシャル情報が来ることを期待します。: + +```js +// 📁 admin.js +export let config = { }; + +export function sayHi() { + alert(`Ready to serve, ${config.user}!`); +} +``` + +ここで、`admin.js` は `config` オブジェクトをエクスポートします(初期は空ですが、デフォルトプロパティもある場合もあります)。 + +次に `init.js` 、我々のアプリの最初のスクリプトで、`config` をインポートし、`config.user` を設定します: + +```js +// 📁 init.js +import {config} from './admin.js'; +config.user = "Pete"; +``` + +...これでモジュール `admin.js` は構成されました。 + +以降のインポートはこれを呼び出すことができ、現在のユーザが正しく表示されます: + +```js +// 📁 another.js +import {sayHi} from './admin.js'; + +sayHi(); // Ready to serve, *!*Pete*/!*! +``` + + +### import.meta + +オブジェクト `import.meta` は現在のモジュールに関する情報を含んでいます。 + +この内容は環境に依存します。ブラウザでは、スクリプトの url、HTML 内であれば現在のウェブページの url を含んでいます。: + +```html run height=0 + +``` + +### モジュールでは、最上位の "this" は undefined です + +これはささいな特徴ですが、完全性のために言及しておきます。 + +モジュールでは、最上位の `this` は undefined です。 + +`this` がグローバルオブジェクトである非モジュールスクリプトとの比較です:。 + +```html run height=0 + + + +``` + +## ブラウザ固有の特徴 + +通常のスクリプトと比べて、`type="module"` を持つスクリプトには、ブラウザ固有の違いもいくつかあります。 + +もし初めて読んでいる場合、またはブラウザで JavaScript を使用していない場合はスキップしても構いません。 + +### モジュールスクリプトは遅延されます + +モジュールスクリプトは外部スクリプトとインラインスクリプト両方で、*常に* 遅延され、`defer` 属性(チャプター [](info:onload-ondomcontentloaded) で説明しています)と同じ効果を持ちます。 + +つまり: +- 外部モジュールスクリプト ` + +以下の通常のスクリプトと比較してください: + + + + +``` + +注意: 実際には1つ目のスクリプトの前に2つ目のスクリプトが動作します! なので、最初に `undefined` が表示され、その後 `object` が表示されます。 + +これは、モジュールが遅延されているためです。通常のスクリプトはすぐに実行するので、最初に出力されます。 + +モジュールを使うときは、JavaScript アプリケーションが準備できる前に HTML ドキュメントが表示できることに注意してください。一部の機能はまだ機能しない可能性があります。透明なオーバーレイ、または "ローディング"を配置する、もしくはそれ以外の方法で訪問者が混乱しないようにする必要があります。 + +### Async はインラインスクリプトで動作します + +非モジュールスクリプトの場合、`async` 属性は外部スクリプトでのみ動作します。Async スクリプトは、他のスクリプトやHTMLドキュメントとは関係なく、準備ができ次第すぐに実行されます。 + +モジュールスクリプトの場合、インラインスクリプトでも動作します。 + +例えば、下のスクリプトは `async` があるので、何かを待つことはありません。 + +それは、たとえ HTMl ドキュメントがまだ完了していない場合や、他のスクリプトがまだ保留の場合でも、インポート( `./analytics.js` の取得)を行い、準備ができたときに実行します。 + +これはカウンタや広告、ドキュメントレベルのイベントリスナなど、何にも依存しない機能に適しています。 + +```html + + + +``` + +### 外部スクリプト + +外部モジュールスクリプトには、2つの大きな違いがあります。: + +1. 同じ `src` の外部スクリプトは一度だけ実行されます: + ```html + + + + ``` + +2. 別のドメインから取得された外部スクリプトは[CORS](mdn:Web/HTTP/CORS) ヘッダを必要とします。言い換えると、モジュールスクリプトが別のドメインから取得された場合、リモートサーバはその取得が許可されていることを示すために、ヘッダ `Access-Control-Allow-Origin: *` (`*` の代わりに取得するドメインを指定する場合もあります)を提供しなければなりません。 + ```html + + + + ``` + + これにより、デフォルトでセキュリティが向上します。 + +### ベア(剥き出しの) モジュールは許可されていません + +ブラウザでは、`import` は相対URLか絶対URLどちらかの取得が必須です。パスがないモジュールは "bare" モジュールと呼ばれます。このようなモジュールは `import` では許可されていません。 + +例えば、この `import` は無効です: +```js +import {sayHi} from 'sayHi'; // Error, "bare" module +// モジュールは、例えば './sayHi.js' またはモジュールの場所でなければなりません +``` + +Node.js やバンドルツールのような特定の環境では、モジュールを見つけるための独自の方法や、それらを調整するためのフックがあるため、剥き出しのモジュールを使用することができます。しかしブラウザではまだベアモジュールはサポートされていません。 + +### 互換性, "nomodule" + +古いブラウザは `type="module"` を理解しません。未知のタイプのスクリプトは単に無視されます。それらには、`nomodule` 属性を使って、フォールバックを提供することが可能です。: + +```html run + + + +``` + +## ビルドツール + +現実には、ブラウザモジュールが "生" の形式で使用されることはほとんどありません。通常、それらを [Webpack](https://webpack.js.org/) などの特別なツールを使って一緒にまとめて、プロダクションサーバにデプロイします。 + +バンドラーを使用する利点の1つは、それらはモジュールをどのように解決するかについてより多くの制御を与えることができ、CSS/HTML モジュールのようにベアモジュールやその他のことを可能にします。 + +ビルドツールは次のことを行います。: + +1. HTML の ` +``` + +とはいえ、ネイティブモジュールも使用可能です。したがって、ここでは Webpack を使用しません: 後で構成できます。 + +## サマリ + +まとめると、コアの概念は次の通りです: + +1. モジュールはファイルです。`import/export` を機能させるには、ブラウザは ` diff --git a/1-js/13-modules/01-modules-intro/say.view/say.js b/1-js/13-modules/01-modules-intro/say.view/say.js new file mode 100644 index 0000000000..198a3be6da --- /dev/null +++ b/1-js/13-modules/01-modules-intro/say.view/say.js @@ -0,0 +1,3 @@ +export function sayHi(user) { + return `Hello, ${user}!`; +} diff --git a/1-js/13-modules/01-modules-intro/scopes-working.view/hello.js b/1-js/13-modules/01-modules-intro/scopes-working.view/hello.js new file mode 100644 index 0000000000..6c087ea81a --- /dev/null +++ b/1-js/13-modules/01-modules-intro/scopes-working.view/hello.js @@ -0,0 +1,3 @@ +import {user} from './user.js'; + +document.body.innerHTML = user; // John diff --git a/1-js/13-modules/01-modules-intro/scopes-working.view/index.html b/1-js/13-modules/01-modules-intro/scopes-working.view/index.html new file mode 100644 index 0000000000..b78f759126 --- /dev/null +++ b/1-js/13-modules/01-modules-intro/scopes-working.view/index.html @@ -0,0 +1,2 @@ + + diff --git a/1-js/13-modules/01-modules-intro/scopes-working.view/user.js b/1-js/13-modules/01-modules-intro/scopes-working.view/user.js new file mode 100644 index 0000000000..d289329c61 --- /dev/null +++ b/1-js/13-modules/01-modules-intro/scopes-working.view/user.js @@ -0,0 +1 @@ +export let user = "John"; diff --git a/1-js/13-modules/01-modules-intro/scopes.view/hello.js b/1-js/13-modules/01-modules-intro/scopes.view/hello.js new file mode 100644 index 0000000000..714aafa1f1 --- /dev/null +++ b/1-js/13-modules/01-modules-intro/scopes.view/hello.js @@ -0,0 +1 @@ +alert(user); // no such variable (each module has independent variables) diff --git a/1-js/13-modules/01-modules-intro/scopes.view/index.html b/1-js/13-modules/01-modules-intro/scopes.view/index.html new file mode 100644 index 0000000000..a87e96fdfb --- /dev/null +++ b/1-js/13-modules/01-modules-intro/scopes.view/index.html @@ -0,0 +1,3 @@ + + + diff --git a/1-js/13-modules/01-modules-intro/scopes.view/user.js b/1-js/13-modules/01-modules-intro/scopes.view/user.js new file mode 100644 index 0000000000..12ec850d9a --- /dev/null +++ b/1-js/13-modules/01-modules-intro/scopes.view/user.js @@ -0,0 +1 @@ +let user = "John"; diff --git a/1-js/13-modules/02-import-export/article.md b/1-js/13-modules/02-import-export/article.md new file mode 100644 index 0000000000..d4968f6ea6 --- /dev/null +++ b/1-js/13-modules/02-import-export/article.md @@ -0,0 +1,436 @@ +# エクスポートとインポート + +エクスポート(export)とインポート(import)ディレクティブにはいくつかの構文パターンがあります。 + +前章ではシンプルな使用例を見ました。ここではより多くの例を見ていきましょう。 + +## 宣言の前の export + +変数、関数、クラスのいずれかであれば、その前に `export` を置くことで、エクスポート対象として任意の宣言にラベル付けすることができます。 + +例えば、ここではすべてのエクスポートは有効です: + +```js +// 配列のエクスポート +*!*export*/!* let months = ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; + +// 定数のエクスポート +*!*export*/!* const MODULES_BECAME_STANDARD_YEAR = 2015; + +// クラスのエクスポート +*!*export*/!* class User { + constructor(name) { + this.name = name; + } +} +``` + +````smart header="export class/function の後にセミコロンはありません" +クラスや関数の前の `export` はそれを [関数式](info:function-expressions) にはしないことに注意してください。エクスポートされていますが、依然として関数宣言です。 + +ほとんどの JavaScript のスタイルガイドは、関数とクラス宣言の後のセミコロンは推奨しません。 + +そういうわけで、`export class` と `export function` の末尾にはセミコロンがありません。 + +```js +export function sayHi(user) { + alert(`Hello, ${user}!`); +} *!* // 末尾に ; はありません */!* +``` + +```` + +## 宣言とは別に export する + +別に `export` を記述することもできます。 + +ここでは、最初に宣言をし、その後エクスポートしています: + +```js +// 📁 say.js +function sayHi(user) { + alert(`Hello, ${user}!`); +} + +function sayBye(user) { + alert(`Bye, ${user}!`); +} + +*!* +export {sayHi, sayBye}; // エクスポートされた変数のリスト +*/!* +``` + +...あるいは、技術的には関数の上に `export` を置くこともできます。 + +## import * + +通常は、次のようにインポートするものの一覧を波括弧 `import {...}` に置きます。: + +```js +// 📁 main.js +*!* +import {sayHi, sayBye} from './say.js'; +*/!* + +sayHi('John'); // Hello, John! +sayBye('John'); // Bye, John! +``` + +しかし、import の数が多い場合、`import * as ` を使用してオブジェクトとしてすべてをインポートすることができます。例: + +```js +// 📁 main.js +*!* +import * as say from './say.js'; +*/!* + +say.sayHi('John'); +say.sayBye('John'); +``` + +一見すると、記述量も少なく、非常にクールに思えます。そもそも、なぜインポートが必要なものを明示的にリストする必要があるのでしょう? + +それにはいくつかの理由があります。 + +1. 何をインポートするかを明示的にリストすることで、より短い名前にできます: `say.sayHi()` の代わりに `sayHi()`。 +2. 明示的なインポートの一覧はコード構造の見通しをよりよくします。: 何がどこで使われているか。それはコードをサポートし、リファクタリングをより簡単にします。 + +```smart header="インポートし過ぎることを気にしないでください" +現代のビルドツール ([webpack](http://webpack.github.io) など) はモジュールをまとめ、読み込みを高速化するために最適化を行ったり、未使用なものを削除します。 + +例えば、巨大なコードライブラリから `import * as library` を行い、いくつかのメソッドのみを使用する場合、未使用のものは最適化されたバンドルには [含まれません](https://github.com/webpack/webpack/tree/main/examples/harmony-unused#examplejs)。 +``` + +## import "as" + +異なる名前でインポートするために `as` を使うこともできます。 + +例えば、簡潔にするために `sayHi` をローカル変数 `hi` にインポートしましょう。`sayBye` も同様です。: + +```js +// 📁 main.js +*!* +import {sayHi as hi, sayBye as bye} from './say.js'; +*/!* + +hi('John'); // Hello, John! +bye('John'); // Bye, John! +``` + +## Export "as" + +同様の構文は `export` にも存在します。 + +関数を `hi` と `bye` としてエクスポートしましょう。: + +```js +// 📁 say.js +... +export {sayHi as hi, sayBye as bye}; +``` + +今、`hi` と `bye` は外部にとって公式な名前になります。: + +```js +// 📁 main.js +import * as say from './say.js'; + +say.*!*hi*/!*('John'); // Hello, John! +say.*!*bye*/!*('John'); // Bye, John! +``` + +## export default + +実際には、主に 2 種類のモジュールがあります。 + +1. 上記の `say.js` のようなライブラリ、関数のパックを含むモジュール +2. 単一のエンティティを宣言するモジュール。例: モジュール `user.js` は `class User` のみをエクスポートします。 + +ほとんどの場合、すべての "もの" が自身のモジュールに存在するように、2 番目のアプローチが好まれます。 + +当然のことながら、モジュールシステムでは、すべてが独自のモジュールになるため、多くのファイルが必要となります。が、それはまったく問題ではありません。実際には、ファイルが良く名前付けされ、フォルダに構造化されていれば、コードのナビゲーションはとても簡単になります。 + +モジュールは、特別な `export default` ("デフォルトエクスポート") 構文を提供し、"モジュール毎に 1 つのもの" のように見栄えを良くします。 + +エクスポートするエンティティの前に `export default` を置きます: + +```js +// 📁 user.js +export *!*default*/!* class User { // "default" を追加するだけ + constructor(name) { + this.name = name; + } +} +``` + +ファイルごとに 1 つだけ `export default` が存在する場合があります。 + +...そして波括弧なしでそれをインポートします: + +```js +// 📁 main.js +import *!*User*/!* from './user.js'; // {User} ではなく User + +new User('John'); +``` + +波括弧なしのインポートは見栄えがよくなります。モジュールを使い始めるときによくある間違いは、波括弧を忘れてしまうことです。なので、覚えておいてください。`import` は名前付けインポートの場合には波括弧が必要であり、デフォルトインポートの場合には不要です。 + +| 名前付きエクスポート | デフォルトエクスポート | +|--------------|----------------| +| `export class User {...}` | `export default class User {...}` | +| `import {User} from ...` | `import User from ...`| + +技術的には、1 つのモジュールの中で、デフォルトと名前付きエクスポート両方をもたせることもできますが、実際には、通常は混在させません。モジュールは名前付きエクスポート、あるいはデフォルトいずれかを持ちます。 + +ファイルごとに最大で 1 つのデフォルトエクスポートがあり、エクスポートされたエンティティには名前がない場合があります。 + +例えば、これらはすべて有効なデフォルトエクスポートです: + +```js +export default class { // クラス名なし + constructor() { ... } +} +``` + +```js +export default function (user) { // 関数名なし + alert(`Hello, ${user}!`); +} +``` + +```js +// 変数の作成なしで単一値のエクスポート +export default ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; +``` + +これは問題ありません。なぜなら `export default` はファイル毎に1つのみだけだからです。そのため、`import` は何をインポートすべきか常に知っています。 + +`default` がない場合はエラーになります。: + +```js +export class { // Error! (非デフォルトエクスポートは名前が必要です) + constructor() {} +} +``` + +### "default" 名 + +状況によっては、"default" というキーワードはデフォルトエクスポートを参照するために使用されます。 + +例えば、ある関数をその定義とは別にエクスポートする場合です: + +```js +function sayHi(user) { + alert(`Hello, ${user}!`); +} + +// 関数の前に "export default" を追加する場合と同じです +export { sayHi as default }; +``` + +あるいは、モジュール `user.js` が 1 つのメインの "デフォルト" のものと、いくつかの名前付きのものをエクスポートする場合(めったにありませんが起こりえます)、次のようになります: + +```js +// 📁 user.js +export default class User { + constructor(name) { + this.name = name; + } +} + +export function sayHi(user) { + alert(`Hello, ${user}!`); +} +``` + +これは名前付きと一緒にデフォルトエクスポートをインポートする方法です: + +```js +// 📁 main.js +import {*!*default as User*/!*, sayHi} from './user.js'; + +new User('John'); +``` + +そして、オブジェクトとして `*` ですべてをインポートする場合、`default` プロパティはまさにデフォルトエクスポートです: + +```js +// 📁 main.js +import * as user from './user.js'; + +let User = user.default; +new User('John'); +``` + +### デフォルトエクスポートを使うべきですか? + +名前付きエクスポートは明示的です。それらは正確にインポートするものを命名するので、そこから情報を得ることができます。これは良いことです。 + +また、名前付きエクスポートは、インポートするのに正確に正しい名前を使うことを強制します。 + +```js +import {User} from './user.js'; +// import {MyUser} は動作しません。名前は {User} でなければなりません。 +``` + +...一方、デフォルトエクスポートの場合、インポート時に常に独自の名前を作成する必要があります。: + +```js +import User from './user.js'; // 動作します +import MyUser from './user.js'; // 動作します +// なにでもインポートでき、それは動作します +``` + +そのため、チームメンバは同じものに異なる名前を使用することができてしまうため、そこには誤用される余地があります。 + +通常、それを避け、コードの一貫性を保つため、インポートされた変数はファイル名に対応するべきであるという規則があります。例えば: + +```js +import User from './user.js'; +import LoginForm from './loginForm.js'; +import func from '/path/to/func.js'; +... +``` + +それでも、チームによってはこれをデフォルトエクスポートの重大な欠点だと考えるかもしれません。この場合は常に名前付きエクスポートを使用することが好ましいです。たとえ単一のものだけがエクスポートされるとしても、`default` なしで名前付きでエクスポートします。 + +これは、再エクスポート(後述)を少しだけ簡単にもします。 + +## 再エクスポート + +"再エクスポート" 構文 `export ... from ...` を使うと、インポートをした直後にそれらを(場合により別の名前で)エクスポートすることができます。: + +```js +export {sayHi} from './say.js'; // sayHi を再エクスポート + +export {default as User} from './user.js'; // default を再エクスポート +``` + +なぜこれが必要なのでしょう?実用的なユースケースを見てみましょう。 + +"パッケージ" を書いていると想像してください。: 多くのモジュールを含むフォルダで、一部の機能が外部にエクスポートされています(NPM のようなツールはこのようなパッケージの公開と配布を可能にしますが、それらを使用する必要はありません)。また、多くのモジュールは他のパッケージのモジュールで内部的に使用するための単なる "ヘルパー" です。 + +ファイル構造は次のようになります: +``` +auth/ + index.js + user.js + helpers.js + tests/ + login.js + providers/ + github.js + facebook.js + ... +``` + +単一のエントリポイント経由でパッケージの機能を公開したいです。 + +つまり、我々のパッケージを利用したい人は "メインファイル" `auth/index.js` からのみインポートします。 + +次のようになります: + +```js +import {login, logout} from 'auth/index.js' +``` + +"メインファイル" `auth/index.js` はパッケージで提供したいすべての機能をエクスポートします。 + +この考えは、部外者(我々のパッケージを使う開発者)は、その内部構造に干渉する必要はないということです。彼らは我々のパッケージフォルダの中のファイルを検索するべきではありません。我々は `auth/index.js` に必要なものだけをエクスポートし、残りの部分は詮索好きな目から隠れたままにします。 + +いま、実際にエクスポートされた機能はパッケージ内に散らかっているので、それらを集めて `auth/index.js` で "再エクスポート" します。: + +```js +// 📁 auth/index.js + +// login/logout をインポートし、すぐにエクスポートします +import {login, logout} from './helpers.js'; +export {login, logout}; + +// User として default をインポートし、エクスポートします +import User from './user.js'; +export {User}; +... +``` + +これで、我々のパッケージの利用者は `import {login} from "auth/index.js"` ができます。 + +構文 `export ... from ...` はこのようなインポート - エクスポートの短縮記法です: + +```js +// 📁 auth/index.js +// login/logout の再エクスポート +export {login, logout} from './helpers.js'; + +// User で default エクスポートを再エクスポート +export {default as User} from './user.js'; +... +``` + +````warn header="デフォルトの再エクスポートは用心が必要です" +注意: `export User from './user.js'` は動作しません。実際には構文エラーです。デフォルトエクスポートを再エクスポートするには、明示的に `{default as ...}` と言及しなければなりません。上の例のように。 + +また、もう1つ奇妙なことがあります。: `export * from './user.js'` は名前付けエクスポートのみを再エクスポートし、デフォルトは含まれていません。改めて言いますが、明示的に言及する必要があります。 + +例えば、すべてを再エクスポートするには、2つの文が必要になります。: + +```js +export * from './module.js'; // to re-export named exports +export {default} from './module.js'; // to re-export default +``` + +デフォルトは再エクスポートするときのみ明示的な言及が必要です。: `import * as obj` は動作します。これは `obj.default` としてデフォルトエクスポートをインポートします。なので、インポートとエクスポートの間には少し非対称性があります。 +```` + +## サマリ + +`export` には以下の種類があります: + +- 宣言の前: + - `export [default] class/function/variable ...` +- スタンドアロン: + - `export {x [as y], ...}`. +- 再エクスポート: + - `export {x [as y], ...} from "mod"` + - `export * from "mod"` (デフォルトは再エクスポートしない). + - `export {default [as y]} from "mod"` (デフォルトの再エクスポート). + +Import: + +- モジュールから名前付きエクスポート: + - `import {x [as y], ...} from "mod"` +- デフォルトエクスポート: + - `import x from "mod"` + - `import {default as x} from "mod"` +- すべて: + - `import * as obj from "mod"` +- モジュールの取得/評価のみでインポートはしない: + - `import "mod"` + +import/export 文を他のコードの前後に置くことができ、どちらでも同じ結果になります。 + +なので、これも技術的には問題ありません: + +```js +sayHi(); + +import {sayHi} from './say.js'; // ファイルの末尾で import +``` + +実際には、インポートは通常ファイルの先頭にありますが、それは利便性のためだけです。 + +**import/export 文は `{...}` の中では動作しないことに注意してください** + +このような条件付きのインポートは動作しません: + +```js +if (something) { + import {sayHi} from "./say.js"; // Error: import must be at top level +} +``` + +...しかし仮に本当に条件に応じてなにかをインポートする必要がある場合はどうなるでしょう?あるいは、本当に必要なときに要求に応じてモジュールをロードするような場合です。 + +次のチャプターではダイナミックインポートを見ていきます。 diff --git a/1-js/13-modules/03-modules-dynamic-imports/article.md b/1-js/13-modules/03-modules-dynamic-imports/article.md new file mode 100644 index 0000000000..71dc3b67a3 --- /dev/null +++ b/1-js/13-modules/03-modules-dynamic-imports/article.md @@ -0,0 +1,98 @@ +# Dynamic imports(ダイナミックインポート) + +前の章で説明したエクスポートとインポート文は "static(静的)" と呼ばれます。この構文は非常にシンプルかつ厳密です。 + +まず `import` の任意のパラメータを動的に生成することはできません。 + +モジュールパスはプリミティブな文字列でなければならず、関数呼び出しもできません。これは動作しません。: + +```js +import ... from *!*getModuleName()*/!*; // Error, string だけが許可されています +``` + +次に、条件に応じたインポートや、実行時にインポートすることはできません。: + +```js +if(...) { + import ...; // Error, 許可されていません! +} + +{ + import ...; // Error, 任意のブロックに import を置くことはできません +} +``` + +これは、`import/export` はコード構造のバックボーンを提供することを目的としているためです。コード構造は分析することができ、特別なツールを利用してモジュールを集め一つまとめることができ、未使用のエクスポートは除去されます(tree-shaken)。これはインポート/エクスポートがすべてが固定されているがゆえに可能なことです。 + +では、どのようにしてモジュールを動的に、オンデマンドでインポートするのでしょう? + +## import() 式 + +`import(module)` 式は、モジュールを読み込み、モジュールがもつすべてのエクスポートを含むモジュールオブジェクトになる promise を返します。 + +コードの任意の場所で動的に利用できます。以下は例です: + +```js run +let modulePath = prompt("Module path?"); + +import(modulePath) + .then(obj => ) + .catch(err => ) +``` + +あるいは、async function 内であれば `let module = await import(modulePath)` とすることができます。 + +例えば、次のようなモジュール `say.js` があるとします: + +```js +// 📁 say.js +export function hi() { + alert(`Hello`); +} + +export function bye() { + alert(`Bye`); +} +``` + +...動的インポートは次のようにできます: + +```js +let {hi, bye} = await import('./say.js'); + +hi(); +bye(); +``` + +また、`say.js` が default export を持っている場合は次のようになります: + +```js +// 📁 say.js +export default function() { + alert("Module loaded (export default)!"); +} +``` + +...そこへアクセスするには、モジュールオブジェクトの `default` プロパティを使用します。: + +```js +let obj = await import('./say.js'); +let say = obj.default; +// あるいは、1行で: let {default: say} = await import('./say.js'); + +say(); +``` + +ここに完全な例があります: + +[codetabs src="say" current="index.html"] + +```smart +ダイナミックインポートは通常のスクリプトで動作するので、`script type="module"` は必要ありません。 +``` + +```smart +`import()` は一見すると関数呼び出しに見えますが、たまたま括弧を使用している特別な構文です(`super()` と同様です)。 + +したがって、変数に `import` をコピーしたり、`call/apply` を使用することはできません。関数ではないからです。 +``` diff --git a/1-js/13-modules/03-modules-dynamic-imports/say.view/index.html b/1-js/13-modules/03-modules-dynamic-imports/say.view/index.html new file mode 100644 index 0000000000..80909cf94d --- /dev/null +++ b/1-js/13-modules/03-modules-dynamic-imports/say.view/index.html @@ -0,0 +1,10 @@ + + + diff --git a/1-js/13-modules/03-modules-dynamic-imports/say.view/say.js b/1-js/13-modules/03-modules-dynamic-imports/say.view/say.js new file mode 100644 index 0000000000..cff234b7c5 --- /dev/null +++ b/1-js/13-modules/03-modules-dynamic-imports/say.view/say.js @@ -0,0 +1,11 @@ +export function hi() { + alert(`Hello`); +} + +export function bye() { + alert(`Bye`); +} + +export default function() { + alert("Module loaded (export default)!"); +} diff --git a/1-js/13-modules/index.md b/1-js/13-modules/index.md new file mode 100644 index 0000000000..b8bd8ecea8 --- /dev/null +++ b/1-js/13-modules/index.md @@ -0,0 +1,2 @@ + +# モジュール diff --git a/1-js/99-js-misc/01-proxy/01-error-nonexisting/solution.md b/1-js/99-js-misc/01-proxy/01-error-nonexisting/solution.md new file mode 100644 index 0000000000..099664a1d6 --- /dev/null +++ b/1-js/99-js-misc/01-proxy/01-error-nonexisting/solution.md @@ -0,0 +1,23 @@ + +```js run +let user = { + name: "John" +}; + +function wrap(target) { + return new Proxy(target, { + get(target, prop, receiver) { + if (prop in target) { + return Reflect.get(target, prop, receiver); + } else { + throw new ReferenceError(`Property doesn't exist: "${prop}"`) + } + } + }); +} + +user = wrap(user); + +alert(user.name); // John +alert(user.age); // Error: Property doesn't exist +``` diff --git a/1-js/99-js-misc/01-proxy/01-error-nonexisting/task.md b/1-js/99-js-misc/01-proxy/01-error-nonexisting/task.md new file mode 100644 index 0000000000..732417938d --- /dev/null +++ b/1-js/99-js-misc/01-proxy/01-error-nonexisting/task.md @@ -0,0 +1,32 @@ +# 存在しないプロパティの読み取りエラー + +通常、存在しないプロパティへの参照をすると `undefined` が返ってきます。 + +代わりに、存在しないプロパティへの参照時にはエラーをスローするようなプロキシを作成してください。 + +これはプログラミングのミスを早期に検出するのに便利です。 + +オブジェクト `target` を取り、この機能を追加するプロキシを返す関数 `wrap(target)` を実装してください。 + +次のように動作するようにしてください: + +```js +let user = { + name: "John" +}; + +function wrap(target) { + return new Proxy(target, { +*!* + /* your code */ +*/!* + }); +} + +user = wrap(user); + +alert(user.name); // John +*!* +alert(user.age); // Error: Property doesn't exist +*/!* +``` diff --git a/1-js/99-js-misc/01-proxy/02-array-negative/solution.md b/1-js/99-js-misc/01-proxy/02-array-negative/solution.md new file mode 100644 index 0000000000..221ea4ce2a --- /dev/null +++ b/1-js/99-js-misc/01-proxy/02-array-negative/solution.md @@ -0,0 +1,19 @@ + +```js run +let array = [1, 2, 3]; + +array = new Proxy(array, { + get(target, prop, receiver) { + if (prop < 0) { + // arr[1] のようにアクセスしても + // prop は文字列なので、数値に変換する必要があります + prop = +prop + target.length; + } + return Reflect.get(target, prop, receiver); + } +}); + + +alert(array[-1]); // 3 +alert(array[-2]); // 2 +``` diff --git a/1-js/99-js-misc/01-proxy/02-array-negative/task.md b/1-js/99-js-misc/01-proxy/02-array-negative/task.md new file mode 100644 index 0000000000..351e4ebf34 --- /dev/null +++ b/1-js/99-js-misc/01-proxy/02-array-negative/task.md @@ -0,0 +1,33 @@ + +# Accessing array[-1] + +プログラム言語によっては、負の値を使って配列要素にアクセスすることが可能で、この場合は末尾から数えられます。 + +このようになります。 + +```js +let array = [1, 2, 3]; + +array[-1]; // 3, 最後の要素 +array[-2]; // 2, 最後から1つ前 +array[-3]; // 1, 最後から2つ前 +``` + +つまり、`array[-N]` は `array[array.length - N]` と同じです。 + +この挙動を実装するプロキシを作成しましょう。 + +次のように動作します: + +```js +let array = [1, 2, 3]; + +array = new Proxy(array, { + /* your code */ +}); + +alert( array[-1] ); // 3 +alert( array[-2] ); // 2 + +// 他の配列の機能は "そのまま" 動作すべきです +``` diff --git a/1-js/99-js-misc/01-proxy/03-observable/solution.md b/1-js/99-js-misc/01-proxy/03-observable/solution.md new file mode 100644 index 0000000000..4937a547c9 --- /dev/null +++ b/1-js/99-js-misc/01-proxy/03-observable/solution.md @@ -0,0 +1,40 @@ +解決策は2つのパートで構成されます: + +1. `.observe(handler)` が呼ばれたときは、後で `handler` が呼び出せるように、ハンドラをどこかに覚えておく必要があります。シンボルをプロパティのキーとして使用することで、ハンドラをオブジェクトに格納できます。 +2. 変更時にハンドラを呼ぶための `set` トラップを持つプロキシが必要です。 + +```js run +let handlers = Symbol('handlers'); + +function makeObservable(target) { + // 1. ハンドラの格納場所の初期化 + target[handlers] = []; + + // 後々の呼び出しのため、配列にハンドラ関数を格納 + target.observe = function(handler) { + this[handlers].push(handler); + }; + + // 2. 変更を処理するプロキシを作成 + return new Proxy(target, { + set(target, property, value, receiver) { + let success = Reflect.set(...arguments); // 操作をオブジェクトに転送 + if (success) { // プロパティの設定でエラーがなければ + // すべてのハンドラを呼び出す + target[handlers].forEach(handler => handler(property, value)); + } + return success; + } + }); +} + +let user = {}; + +user = makeObservable(user); + +user.observe((key, value) => { + alert(`SET ${key}=${value}`); +}); + +user.name = "John"; +``` diff --git a/1-js/99-js-misc/01-proxy/03-observable/task.md b/1-js/99-js-misc/01-proxy/03-observable/task.md new file mode 100644 index 0000000000..1d5bf8445d --- /dev/null +++ b/1-js/99-js-misc/01-proxy/03-observable/task.md @@ -0,0 +1,28 @@ + +# Observable + +プロキシを返すことで、"オブジェクトを監視可能にする" 関数 `makeObservable(target)` を作成してください。 + +このように動作します: + +```js run +function makeObservable(target) { + /* your code */ +} + +let user = {}; +user = makeObservable(user); + +user.observe((key, value) => { + alert(`SET ${key}=${value}`); +}); + +user.name = "John"; // alerts: SET name=John +``` + +つまり、`makeObservable` により返却されるオブジェクトは元のオブジェクトのように見えますが、任意のプロパティ変更時に呼び出される `handler` 関数をセットするメソッド `observe(handler)` を持ちます。 + +プロパティを変更したときはいつでもプロパティの名前と値と一緒に `handler(key, value)` が呼ばれます。 + +P.S. このタスクでは、プロパティの書き込みにだけ注目してください。他の操作も同様の方法で実装することはできます。 + diff --git a/1-js/99-js-misc/01-proxy/article.md b/1-js/99-js-misc/01-proxy/article.md new file mode 100644 index 0000000000..712b7811d1 --- /dev/null +++ b/1-js/99-js-misc/01-proxy/article.md @@ -0,0 +1,1034 @@ +# Proxy と Reflect + +`Proxy` オブジェクトは別のオブジェクトをラップし、プロパティやその他の読み取り/書き込みなどの操作をインターセプトします。必要に応じてそれらを独自に処理したり、オブジェクトが透過的にそれらを処理できるようにします。 + +Proxy は多くのライブラリや一部のブラウザフレームワークで使われています。この章では、多くの実践的なアプリケーションを紹介します。 + +## Proxy + +構文: + +```js +let proxy = new Proxy(target, handler) +``` + +- `target` -- ラップするオブジェクトです。関数含め何でもOKです。 +- `handler` -- プロキシ設定: 操作をインターセプトするメソッドである "トラップ" をもつオブジェクトです。例: `get` トラップは `target` のプロパティの読み取り用、`set` トラップは、`target` へのプロパティ書き込み用、など。 + +`proxy` の操作では、`handler` に対応するトラップがある場合はそれが実行されます。それ以外の場合は、操作は `target` で実行されます。 + +最初の例として、トラップなしでプロキシを作ってみましょう。: + +```js run +let target = {}; +let proxy = new Proxy(target, {}); // 空のハンドラ + +proxy.test = 5; // プロキシへの書き込み (1) +alert(target.test); // 5, プロパティが target で現れました! + +alert(proxy.test); // 5, proxy からの読み取ることができます (2) + +for(let key in proxy) alert(key); // test, イテレーションも機能します (3) +``` + +トラップがないので、`proxy` 上のすべての操作は `target` に転送されます。 + +1. 書き込み操作 `proxy.test=` は `target` に値を設定します。 +2. 読み込み操作 `proxy.test` は `target` からの値を返します。 +3. `proxy` のイテレートは、`target` からの値を返します。 + +ご覧の通り、トラップがない場合は `proxy` は `target` に対する透過的なラッパーです。 + +![](proxy.svg) + +`Proxy` は特別な "エキゾチックオブジェクト(exotic object)" です。`Proxy` は独自のプロパティは持っていません。空の `handler` の場合は、透過的に `target` へ操作を転送します。 + +さらに機能を有効にするために、トラップを追加しましょう。 + +これによって、何がインターセプトできるでしょう? + +オブジェクトに対するほとんどの操作に対しては、JavaScript の仕様で いわゆる "内部メソッド" と呼ばれるものがあり、仕様ではそれらがどのように動作するかを最も低レベルで説明しています。例えば、 `[[Get]]` は、プロパティを読み取るための内部メソッドで、`[[Set]]` はプロパティを書き込むための内部メソッド、などです。これらのメソッドは仕様でのみ使用されており、名前を使ってそれらを直接使用することはできません。 + +プロキシのトラップはこれらのメソッドの呼び出しをインターセプトします。これらのメソッドは[Proxy specification](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots) 及び以下の表にリストされています。 + +このテーブルに、すべての内部ソッドに対するトラップがあります: 操作をインターセプトするために `new Proxy` の `handler` パラメータに追加できるメソッド名です: + +| 内部メソッド | ハンドラメソッド | いつ発生するか | +|-----------------|----------------|-------------| +| `[[Get]]` | `get` | プロパティ読み取り時 | +| `[[Set]]` | `set` | プロパティ書き込み時 | +| `[[HasProperty]]` | `has` | `in` 演算子 | +| `[[Delete]]` | `deleteProperty` | `delete` 演算子 | +| `[[Call]]` | `apply` | 関数呼び出し | +| `[[Construct]]` | `construct` | `new` 演算子 | +| `[[GetPrototypeOf]]` | `getPrototypeOf` | [Object.getPrototypeOf](mdn:/JavaScript/Reference/Global_Objects/Object/getPrototypeOf) | +| `[[SetPrototypeOf]]` | `setPrototypeOf` | [Object.setPrototypeOf](mdn:/JavaScript/Reference/Global_Objects/Object/setPrototypeOf) | +| `[[IsExtensible]]` | `isExtensible` | [Object.isExtensible](mdn:/JavaScript/Reference/Global_Objects/Object/isExtensible) | +| `[[PreventExtensions]]` | `preventExtensions` | [Object.preventExtensions](mdn:/JavaScript/Reference/Global_Objects/Object/preventExtensions) | +| `[[DefineOwnProperty]]` | `defineProperty` | [Object.defineProperty](mdn:/JavaScript/Reference/Global_Objects/Object/defineProperty), [Object.defineProperties](mdn:/JavaScript/Reference/Global_Objects/Object/defineProperties) | +| `[[GetOwnProperty]]` | `getOwnPropertyDescriptor` | [Object.getOwnPropertyDescriptor](mdn:/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor), `for..in`, `Object.keys/values/entries` | +| `[[OwnPropertyKeys]]` | `ownKeys` | [Object.getOwnPropertyNames](mdn:/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames), [Object.getOwnPropertySymbols](mdn:/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols), `for..in`, `Object.keys/values/entries` | + +```warn header="Invariants" +JavaScript にはいくつかの不変条件(内部メソッドと トラップによって満たされるべき条件)があります。 + +そのほとんどは戻り値に関してです: +- `[[Set]]` は値が正常に書き込まれた場合には `true` を、そうでなければ `false` を返す必要があります。 +- `[[Delete]]` は値が正常に削除された場合には `true` を、そうでなければ `false` を返す必要があります。 +- ...などです。以下の例で詳しく見ていきます。 + +他にも以下のようないくつかの不変条件があります: +- proxy オブジェクトに適用される `[[GetPrototypeOf]]` は proxy オブジェクトのターゲットオブジェクトに適用される `[[GetPrototypeOf]]` と同じ値を返さなければなりません。つまり、proxy のプロトタイプを参照すると、常にターゲットオブジェクトのプロトタイプが返却される必要があります。 + +traps はこれらの操作をインターセプトできますが、これらのルールには従う必要があります。 + +不変条件は、言語機能の正しさと一貫した動作を保証するものです。完全な不変条件のリストは [仕様](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots)にありますが、変なことをしない限りは違反することはないでしょう。 +``` + +実際の例でそれがどのように動作するのかを見てみましょう。 + +## "get" トラップでのデフォルト値 + +最も一般的なトラップ(traps)はプロパティの読み書きです。 + +読み取りをインターセプトするには、`handler` に `get(target, property, receiver)` が必要です。 + +これはプロパティが読み取られたとき、以下の引数で実行されます。: + +- `target`: `new Proxy` の最初の引数として渡されるターゲットオブジェクトです。 +- `property` -- プロパティ名, +- `receiver` --ターゲットプロパティが getter の場合、`receiver` はその呼び出しの中で `this` として使われるオブジェクトです。通常、これは `proxy` オブジェクト自身(あるいは、proxy から継承している場合は、継承したオブジェクト)です。現時点ではこの引数は不要です。詳細については後ほど説明します。 + +オブジェクトのデフォルト値を実装するのに `get` を使ってみましょう。 + +存在しない値の場合 `0` を返す数値配列を作ります。 + +通常、存在しない値を取得しようとすると `undefined` になりますが、ここでは通常の配列に対して、プロパティが存在しない場合に `0` を返すプロキシでラップします。: + +```js run +let numbers = [0, 1, 2]; + +numbers = new Proxy(numbers, { + get(target, prop) { + if (prop in target) { + return target[prop]; + } else { + return 0; // デフォルト値 + } + } +}); + +*!* +alert( numbers[1] ); // 1 +alert( numbers[123] ); // 0 (このような項目はなし) +*/!* +``` + +ご覧の通り、`get` トラップを使用するのは非常に簡単です。 + +`Proxy` を利用すると、任意の "デフォルト値" 用のロジックを組むことができます。 + +想像してください、フレーズと一緒に翻訳を持つ辞書があるとします: + +```js run +let dictionary = { + 'Hello': 'Hola', + 'Bye': 'Adiós' +}; + +alert( dictionary['Hello'] ); // Hola +alert( dictionary['Welcome'] ); // undefined +``` + +現在、フレーズがない場合、`dictionary` の読み取りは `undefined` を返します。しかし、実際には `undefined` よりも未翻訳のままのフレーズを残すほうがよいです。なので、このような場合に `undefined` ではなく、未翻訳のフレーズを返すようにしましょう。 + +そのためには、`directory` を読み取り操作をインターセプトするプロキシでラップします。: + +```js run +let dictionary = { + 'Hello': 'Hola', + 'Bye': 'Adiós' +}; + +dictionary = new Proxy(dictionary, { +*!* + get(target, phrase) { // 辞書(dictionary)からのプロパティ読み取りをインターセプト +*/!* + if (phrase in target) { // 辞書の中にある場合 + return target[phrase]; // 翻訳を返します + } else { + // そうでなければフレーズをそのまま返します + return phrase; + } + } +}); + +// 辞書で任意のフレーズを検索します +// 辞書にない場合は翻訳されません +alert( dictionary['Hello'] ); // Hola +*!* +alert( dictionary['Welcome to Proxy']); // Welcome to Proxy +*/!* +``` + +````smart +プロキシがどのように変数を上書きするかに注意してください。: + +```js +dictionary = new Proxy(dictionary, ...); +``` + +プロキシはどこでもターゲットオブジェクトを完全に置き換える必要があります。プロキシされた後はターゲットオブジェクトを参照しないでください。参照すると、簡単に台無しになります。 +```` + +## "set" トラップでのバリデーション + +数値専用の配列がほしいとしましょう。別の型の値が追加された場合、エラーにする必要があります。 + +`set` トラップはプロパティが書き込まれたときに発生します。 + +`set(target, property, value, receiver)`: + +- `target`: `new Proxy` の最初の引数として渡されるターゲットオブジェクトです。 +- `property`: プロパティ名 +- `value`: プロパティ値, +- `receiver`: `get` と同様で、setter プロパティに関係します。 + +`set` トラップは設定が成功すると `true` を、それ以外の場合は `false` (`TypeError` が発生)を返す必要があります。 + +新しい値を検証するのに使って見ましょう: + +```js run +let numbers = []; + +numbers = new Proxy(numbers, { // (*) +*!* + set(target, prop, val) { // プロパティの書き込みをインターセプト +*/!* + if (typeof val == 'number') { + target[prop] = val; + return true; + } else { + return false; + } + } +}); + +numbers.push(1); // 追加成功 +numbers.push(2); // 追加成功 +alert("Length is: " + numbers.length); // 2 + +*!* +numbers.push("test"); // TypeError (プロキシの 'set' が false を返却) +*/!* + +alert("This line is never reached (error in the line above)"); +``` + +注目してください: 配列の組み込みの機能は依然として動作します! 値は `push` により追加されました。`length` プロパティは値が追加されたときにオートインクリメントされます。プロキシは何も破壊していません。 + +我々はチェック処理を追加するのに `push` や `unshift` のような、値を追加する配列メソッドを上書きする必要はありません。なぜなら、それらは内部的には `[[Set]]` 操作を使用しており、プロキシによりインターセプトされるからです。 + +したがって、コードはクリーンであり簡潔です。 + +```warn header="`true` を返すのを忘れないでください" +上記のように、維持すべき条件があります。 + +`set` の場合、書き込みの成功に対しては `true` を返さなければなりません。 + +それを忘れたり false を返すと、操作は `TypeError` をトリガーします。 +``` + +## "ownKeys" と "getOwnPropertyDescriptor" によるイテレーション + +`Object.keys`, `for..in` ループ及びオブジェクトプロパティをイテレートする他のほとんどのメソッドは `[[OwnPropertyKeys]]` 内部メソッド(`ownKeys` トラップによりインターセプトされる)を使用してプロパティのリストを取得しています。 + +このようなメソッドの詳細は異なります: +- `Object.getOwnPropertyNames(obj)` は "非" シンボルキーを返します。 +- `Object.getOwnPropertySymbols(obj)` はシンボルキーを返します。 +- `Object.keys/values()` は `enumerable` フラグ(プロパティフラグについては、チャプター に説明があります)を持つ非シンボルのキー/バリュー値を返します。 +- `for..in` は `enumerable` フラグを持つ非シンボルキーとプロトタイプキーをループします。 + +...しかし、これらはすべてその内部メソッドで得られたリストから始まります。 + +以下の例では、`ownKeys` トラップを使用して `user` に対する `for..in` ループを行い、また `Object.keys` や `Object.values` を行っています。これらはアンダースコア `_` で始まるプロパティをスキップします。: + +```js run +let user = { + name: "John", + age: 30, + _password: "***" +}; + +user = new Proxy(user, { +*!* + ownKeys(target) { +*/!* + return Object.keys(target).filter(key => !key.startsWith('_')); + } +}); + +// "ownKeys" は _password を除外します +for(let key in user) alert(key); // name, then: age + +// これらのメソッドへも同じ影響があります: +alert( Object.keys(user) ); // name,age +alert( Object.values(user) ); // John,30 +``` + +これまでのところ、期待通り動作しています。 + +ですが、もしオブジェクトに存在しないキーを返した場合、`Object.keys` はそれをリストしません: + +```js run +let user = { }; + +user = new Proxy(user, { +*!* + ownKeys(target) { +*/!* + return ['a', 'b', 'c']; + } +}); + +alert( Object.keys(user) ); // +``` + +なぜでしょう?理由は簡単です。: `Object.keys` は `enumerable` フラグを持つプロパティだけを返すからです。それを確かめるため、すべてのメソッドに対し内部メソッド `[[GetOwnProperty]]` を呼び出し,[ディスクリプタ](info:property-descriptors) を取得します。すると、ここではプロパティがないので、そのディスクリプタは空であり、`enumerable` フラグがありません。そのため、スキップされます。 + +`Object.keys` がプロパティを返すには、`enumerable` 付きでオブジェクトに存在するか、`[[GetOwnProperty]]`(トラップは `getOwnPropertyDescriptor`)の呼び出しをインターセプトし、`enumerable: true` を持つディスクリプタを返します。 + +これはそのコードです: + +```js run +let user = { }; + +user = new Proxy(user, { + ownKeys(target) { // プロパティのリストを取得するために一度だけ呼ばれます + return ['a', 'b', 'c']; + }, + + getOwnPropertyDescriptor(target, prop) { // プロパティ毎に呼ばれます + return { + enumerable: true, + configurable: true + /* ...other flags, probable "value:..."" */ + }; + } + +}); + +alert( Object.keys(user) ); // a, b, c +``` + +改めて留意してください: `[[GetOwnProperty]]` をインターセプトする必要があるのは、プロパティがオブジェクトにない場合のみです。 + +## "deleteProperty" 及び他のトラップで保護されたプロパティ + +アンダースコア `_` で始まるプロパティやメソッドは内部的なものであるということは、広く知られた慣習です。それらはオブジェクトの外からアクセスされるべきではありません。 + +ですが、技術的には可能です: + +```js run +let user = { + name: "John", + _password: "secret" +}; + +alert(user._password); // secret +``` + +プロキシを使用して、`_` で始まるプロパティへのアクセスを防ぎましょう。 + +次のトラップが必要です: +- `get`: そのようなプロパティの読み込み時にエラーをスロー, +- `set`: 書き込み時にエラーをスロー, +- `deleteProperty`: 削除時にエラーをスロー, +- `ownKeys`: `for..in` や `Object.keys` のようなメソッドから `_` で始まるプロパティを除外 + +これがそのコードです: + +```js run +let user = { + name: "John", + _password: "***" +}; + +user = new Proxy(user, { +*!* + get(target, prop) { +*/!* + if (prop.startsWith('_')) { + throw new Error("Access denied"); + } + let value = target[prop]; + return (typeof value === 'function') ? value.bind(target) : value; // (*) + }, +*!* + set(target, prop, val) { // プロパティの書き込みをインターセプト +*/!* + if (prop.startsWith('_')) { + throw new Error("Access denied"); + } else { + target[prop] = val; + return true; + } + }, +*!* + deleteProperty(target, prop) { // プロパティの削除をインターセプト +*/!* + if (prop.startsWith('_')) { + throw new Error("Access denied"); + } else { + delete target[prop]; + return true; + } + }, +*!* + ownKeys(target) { // プロパティのリストをインターセプト +*/!* + return Object.keys(target).filter(key => !key.startsWith('_')); + } +}); + +// "get" は _password の読み込みを許可しません +try { + alert(user._password); // Error: Access denied +} catch(e) { alert(e.message); } + +// "set" は _password の書き込みを許可しません +try { + user._password = "test"; // Error: Access denied +} catch(e) { alert(e.message); } + +// "deleteProperty" は _password の削除を許可しません +try { + delete user._password; // Error: Access denied +} catch(e) { alert(e.message); } + +// "ownKeys" は _password を除外します +for(let key in user) alert(key); // name +``` + +`(*)` 行の `get` トラップの重要な点に注意してください: + +```js +get(target, prop) { + // ... + let value = target[prop]; +*!* + return (typeof value === 'function') ? value.bind(target) : value; // (*) +*/!* +} +``` + +なぜ関数の場合に `value.bind(target)` を呼び出す必要があるのでしょうか? + +理由は `user.checkPassword()` のようなオブジェクトメソッドは `_password` へアクセスできる必要があるからです。: + +```js +user = { + // ... + checkPassword(value) { + // オブジェクトメソッドは _password へアクセスできなければいけません + return value === this._password; + } +} +``` + + +`user.checkPassword()` の呼び出しはプロキシされた `user` を `this` (ドットの前のオブジェクトが `this` になります)として取得するため、`this._password` へのアクセスを試みると `get` トラップが機能(これはあらゆるプロパティ読み取りでトリガーされます)し、エラーをスローします。 + +そのため、`(*)` の通りオブジェクトメソッドのコンテキストを元のオブジェクトである `target` でバインドします。以降、その呼び出しでは `this` としてトラップのない `target` を使用します。 + +この解決策はたいてい動作しますが、メソッドがプロキシされていないオブジェクトを別の場所に渡す可能性があるため理想的ではありません。これは混乱のもとになります: どこにオリジナルのオブジェクトがあり、どれがプロキシされたものなのか。 + +さらに、オブジェクトが何度もプロキシされる可能性もあります(複数のプロキシがそれぞれ異なる "微調整" をオブジェクトにする場合があります)。また、メソッドにラップされていないオブジェクトを渡した場合、予期しない結果になる可能性もあります。 + +したがって、このようなプロキシは使用しないことを推奨します。 + +```smart header="クラスの private プロパティ" +モダンな JavaScript エンジンはクラスの private プロパティをネイティブにサポートします(`#` から始まります)。これについてはチャプター で記載しています。プロキシは必要ありません。 + +ただし、このようなプロパティにも問題はあります。特にこれらは継承されません。 +``` + +## "has" トラップを使用した "範囲内" + +他の例を見てみましょう。 + +範囲を持つオブジェクトがあります: + +```js +let range = { + start: 1, + end: 10 +}; +``` + +`in` 演算子を使って、 数値が `range` の範囲内にあるかを確認します。 + +`has` トラップは `in` 呼び出しをインターセプトします。 + +`has(target, property)` + +- `target` -- `new Proxy` への最初の引数として渡されるターゲットオブジェクト +- `property` -- プロパティ名 + +デモです: + +```js run +let range = { + start: 1, + end: 10 +}; + +range = new Proxy(range, { +*!* + has(target, prop) { +*/!* + return prop >= target.start && prop <= target.end + } +}); + +*!* +alert(5 in range); // true +alert(50 in range); // false +*/!* +``` + +良い糖衣構文ですね。それに実装もとても簡単です。 + +## Wrapping functions: "apply" + +関数の周りに対しても同様に proxy をラップすることができます。 + +`apply(target, thisArg, args)` トラップはプロキシを関数として呼び出すよう処理をします: + +- `target` はターゲットオブジェクトです(JavaScript では関数はオブジェクトです), +- `thisArg` は `this` の値です +- `args` は引数のリストです + +例えば、チャプター で行った `delay(f, ms)` デコレータを思い出してください。 + +そのチャプターでは、proxy を使わずに実現しました。`delay(f, ms)` の呼び出しは、`ms` ミリ秒後に `f` の呼び出しを行う関数を返しました。 + +これは以前の関数ベースの実装です: + +```js run +function delay(f, ms) { + // タイムアウト後に f への呼び出しを渡すラッパー関数を返します + return function() { // (*) + setTimeout(() => f.apply(this, arguments), ms); + }; +} + +function sayHi(user) { + alert(`Hello, ${user}!`); +} + +// このラップをすると、sahHi 呼び出しは 3秒間遅延します +sayHi = delay(sayHi, 3000); + +sayHi("John"); // Hello, John! (3秒後) +``` + +すでにご覧になったように、これはほぼほぼ機能します。ラッパー関数 `(*)` はタイムアウト後に呼び出しを実行します。 + +しかし、ラッパー関数はプロパティの読み書き操作などは転送しません。ラップした後、`name` や `length` などの元の関数のプロパティへのアクセスは失われます。: + +```js run +function delay(f, ms) { + return function() { + setTimeout(() => f.apply(this, arguments), ms); + }; +} + +function sayHi(user) { + alert(`Hello, ${user}!`); +} + +*!* +alert(sayHi.length); // 1 (function.length は宣言された関数の引数の数を返します) +*/!* + +sayHi = delay(sayHi, 3000); + +*!* +alert(sayHi.length); // 0 (ラッパー後は引数は 0 です) +*/!* +``` + +`Proxy` はすべてをターゲットオブジェクトに転送するので、はるかに強力です。 + +関数ラッピングの代わりに `Proxy` を使って見ましょう: + +```js run +function delay(f, ms) { + return new Proxy(f, { + apply(target, thisArg, args) { + setTimeout(() => target.apply(thisArg, args), ms); + } + }); +} + +function sayHi(user) { + alert(`Hello, ${user}!`); +} + +sayHi = delay(sayHi, 3000); + +*!* +alert(sayHi.length); // 1 (*) プロキシは length 操作をターゲットに転送します +*/!* + +sayHi("John"); // Hello, John! (3秒後) +``` + +結果は同じですが、呼び出しだけでなく、プロキシ上のすべての操作は元の関数に転送されます。そのため、行 `(*)` で `sayHi.length` はラッピング後も正しい値を返します。 + +これで "よりリッチな" ラッパーを手に入れました。 + +他にもトラップはあります: 完全なリストはこのチャプターの最初にのせています。それらの使用パターンは上記と同じです。 + +## Reflect + +`Reflect` は `Proxy` の作成を簡単にする組み込みのオブジェクトです。 + +以前説明したとおり、`[[Get]]`, `[[Set]]` やその他の内部メソッドは仕様上のものであり、直接呼び出すことはできません。 + +`Reflect` オブジェクトはそれをいくらか可能にします。それのもつメソッドは内部メソッドの最小限のラッパーです。 + +ここでは、操作と、それと同じことをする `Reflect` 呼び出しの例を示します: + +| 操作 | `Reflect` 呼び出し | 内部メソッド | +|-----------------|----------------|-------------| +| `obj[prop]` | `Reflect.get(obj, prop)` | `[[Get]]` | +| `obj[prop] = value` | `Reflect.set(obj, prop, value)` | `[[Set]]` | +| `delete obj[prop]` | `Reflect.deleteProperty(obj, prop)` | `[[HasProperty]]` | +| `new F(value)` | `Reflect.construct(F, value)` | `[[Construct]]` | +| ... | ... | ... | + +例: + +```js run +let user = {}; + +Reflect.set(user, 'name', 'John'); + +alert(user.name); // John +``` + +特に、`Reflect` では演算子 (`new`, `delete`...) を関数(`Reflect.construct`, `Reflect.deleteProperty`, ...)として呼び出すことができます。これは興味深い機能ですが、ここでは別に重要な部分があります。 + +**`Proxy` でトラップ可能なすべての内部メソッドに対し、`Reflect` には `Proxy` トラップと同じ名前、引数を持つ対応するメソッドがあります。** + +したがって、`Reflect` を使って操作を元のオブジェクトに転送することができます。 + +この例では、`get` と `set` の両方のトラップが、読み書き操作をオブジェクトへ透過的(存在しないかのように)に転送し、メッセージを表示します。: + +```js run +let user = { + name: "John", +}; + +user = new Proxy(user, { + get(target, prop, receiver) { + alert(`GET ${prop}`); +*!* + return Reflect.get(target, prop, receiver); // (1) +*/!* + }, + set(target, prop, val, receiver) { + alert(`SET ${prop}=${val}`); +*!* + return Reflect.set(target, prop, val, receiver); // (2) +*/!* + } +}); + +let name = user.name; // "GET name" を表示 +user.name = "Pete"; // "SET name=Pete" を表示 +``` + +Here: + +- `Reflect.get` はオブジェクトプロパティを読み取ります。 +- `Reflect.set` はオブジェクトプロパティの書き込みを行い、成功すれば `true` を返します。それ以外の場合は `false` を返します。 + +つまり、すべては単純です: トラップが呼び出しをオブジェクトに転送したい場合、同じ引数で `Reflect.` を呼べばよいです。 + +ほとんどの場合で、`Reflect` を使うことなく同じことができます。例えば、プロパティの読み取り `Reflect.get(target, prop, receiver)` は `target[prop]` に置き換えることができます。ですが、重要な意味合いがあります。 + +### ゲッター(getter)のプロキシ + +なぜ `Reflect.get` が優れている理由を示すデモを見てみましょう。合わせて、なぜ `get/set` が4番目の引数 `receiver` を持っているのか(これは以前は使用していませんでした)も見ていきましょう。 + +`_name` プロパティをもつ `user` オブジェクトがあり、そのゲッターをします: + +これはそのプロキシです: + +```js run +let user = { + _name: "Guest", + get name() { + return this._name; + } +}; + +*!* +let userProxy = new Proxy(user, { + get(target, prop, receiver) { + return target[prop]; + } +}); +*/!* + +alert(userProxy.name); // Guest +``` + +ここでは、`get` トラップは明白です。元のプロパティを返し、他には何もしていません。今回の例ではこれで十分です。 + +今のところすべて問題ありません。では例をもう少し複雑にしてみましょう。 + +`user` から別のオブジェクト `admin` を継承すると、正しくない振る舞いが起きます: + +```js run +let user = { + _name: "Guest", + get name() { + return this._name; + } +}; + +let userProxy = new Proxy(user, { + get(target, prop, receiver) { + return target[prop]; // (*) target = user + } +}); + +*!* +let admin = { + __proto__: userProxy, + _name: "Admin" +}; + +// 期待値: Admin +alert(admin.name); // 出力: Guest (?!?) +*/!* +``` + +`admin.name` の読み取りは `"Guest"` ではなく `"Admin"` を返すべきです! + +何が起きたのでしょうか?継承になにか問題があったのでしょうか? + +ですが、プロキシを削除するとすべて期待通りに動作します。 + +問題は行 `(*)` のプロキシの中にあります。 + +1. `admin.name` を読み取るとき、`admin` オブジェクトにはそのようなプロパティはないため、検索はそのプロトタイプに進みます。 +2. プロトタイプは `userProxy` です。 +3. プロキシから `name` プロパティを読み取ると、`get` トラップが発生し、行 `(*)` で `target[prop]` により元のオブジェクトから返却されます。 + + `prop` がゲッターである場合、`target[prop]` の呼び出しはコンテキスト `this=target` でコードが実行されます。そのため、結果は元のオブジェクト `target`, つまり `user` からの `this._name` になります。 + +これを修正するには、`get` トラップの3番目の引数である `receiver` が必要です。これによりゲッターに正しい `this` を渡すことができます。今回のケースだと、`admin` です。 + +どうやってゲッターへコンテキストを渡すのでしょう?通常の関数では `call/apply` を使いますが、これはゲッターなので "呼び出される" のではなく、単なるアクセスです。 + +`Reflect.get` はそれをすることができます。これを使うことですべてが上手く動きます。 + +修正されたバリアントです: + +```js run +let user = { + _name: "Guest", + get name() { + return this._name; + } +}; + +let userProxy = new Proxy(user, { + get(target, prop, receiver) { // receiver = admin +*!* + return Reflect.get(target, prop, receiver); // (*) +*/!* + } +}); + + +let admin = { + __proto__: userProxy, + _name: "Admin" +}; + +*!* +alert(admin.name); // Admin +*/!* +``` + +上のコードでは、正しい `this` (つまり `admin`) への参照を維持する `receiver` は、行 `(*)` で `Reflect.get` を使用したゲッターに渡されます。 + +トラップをさらに短く書くこともできます: + +```js +get(target, prop, receiver) { + return Reflect.get(*!*...arguments*/!*); +} +``` + + +`Reflect` 呼び出しはトラップとまったく同じ名前が付けられており、同じ引数を受け付けます。特別にそのように設計されました。 + +したがって、`return Reflect...` は安全かつ考えるまでもない分かりやすい手段で操作を転送することができます。 + +## プロキシの制限 + +プロキシは既存のオブジェクトの動作を最も低いレベルで変更したり微調整する独自の方法を提供します。それでも完璧ではありません。いくつか制限があります。 + +### 組み込みオブジェクト: 内部スロット(Internal slots) + +`Map`, `Set`, `Date`, `Promise` などの多くの組み込みオブジェクトは、いわゆる "内部スロット" を使用します。 + +それらはプロパティに似ていますが、内部で仕様専用の目的で予約されています。例えば、`Map` は内部スロット `[[MapData]]` にアイテムを保存します。組み込みのメソッドは、`[[Get]]/[[Set]]` 内部メソッド経由ではなく、直接アクセスします。そのため、`Proxy` はインターセプトすることができません。 + +内部の話なのに気にする必要はあるのでしょうか? + +ここに問題があります。このような組み込みのオブジェクトがプロキシされると、プロキシはこれらの内部スロットを持たないため、組み込みのメソッドは失敗します。 + +例: + +```js run +let map = new Map(); + +let proxy = new Proxy(map, {}); + +*!* +proxy.set('test', 1); // Error +*/!* +``` + +内部的に、`Map` はすべてのデータを `[[MapData]]` 内部スロットに保存します。プロキシはそのようなスロットはありません。[組み込みのメソッド `Map.prototype.set`](https://tc39.es/ecma262/#sec-map.prototype.set) メソッドは内部プロパティ `this.[[MapData]]` にアクセスしようとしますが、`this=proxy` なので `proxy` 内には見つけることができず失敗します。 + +幸いなことに、修正する方法があります: + +```js run +let map = new Map(); + +let proxy = new Proxy(map, { + get(target, prop, receiver) { + let value = Reflect.get(...arguments); +*!* + return typeof value == 'function' ? value.bind(target) : value; +*/!* + } +}); + +proxy.set('test', 1); +alert(proxy.get('test')); // 1 (works!) +``` + +上の例では、`get` トラップは `map.set` などの関数プロパティをターゲットオブジェクト(`map`)自身にバインドするので、問題なく動作します。 + +これまでの例とは違い、`proxy.set(...)` 内での `this` の値は `proxy` ではなく元の `map` になります。そのため、`set` の内部実装が `this.[[MapData]]` 内部スロットにアクセスするのは成功します。 + +```smart header="`Array` には内部スロットがありません" +注目すべき例外です: 組み込みの `Array` は内部スロットを使用していません。`Array` はずっと以前から存在していたこともあり、歴史的な理由によるものです。 + +したがって配列をプロキシする際にはこのような問題は起こりません。 +``` + +### プライベートフィールド + +似たようなことがプライベートクラスフィールドでも起こります。 + +例えば、`getName()` メソッドはプロキシ後にプライベート `#name` プロパティへアクセスすると壊れます。: + +```js run +class User { + #name = "Guest"; + + getName() { + return this.#name; + } +} + +let user = new User(); + +user = new Proxy(user, {}); + +*!* +alert(user.getName()); // Error +*/!* +``` + +これは、プライベートフィールドが内部スロットを使用して実装されているからです。JavaScript はそれらにアクセスする際、`[[Get]]/[[Set]]` は使用しません。 + +`getName()` の呼び出しでは、`this` の値はプロキシされた `user` であり、プライベートフィールドのスロットを持っていません。 + +この場合も、メソッドをバインドする方法で機能させることができます: + +```js run +class User { + #name = "Guest"; + + getName() { + return this.#name; + } +} + +let user = new User(); + +user = new Proxy(user, { + get(target, prop, receiver) { + let value = Reflect.get(...arguments); + return typeof value == 'function' ? value.bind(target) : value; + } +}); + +alert(user.getName()); // Guest +``` + +ただし、この解決策にも欠点があります。以前説明したとおり、この方法は元のオブジェクトをメソッドに公開するので、メソッドの処理によってはさらにオブジェクトが渡される可能性があり、他のプロキシされた機能を破壊する可能性があります。 + +### Proxy != target + +Proxy と元のオブジェクトは異なるオブジェクトです。これは当然ですね。 + +なので、元のオブジェクトをキーとして使用し、その後プロキシすると、プロキシは見つかりません。: + +```js run +let allUsers = new Set(); + +class User { + constructor(name) { + this.name = name; + allUsers.add(this); + } +} + +let user = new User("John"); + +alert(allUsers.has(user)); // true + +user = new Proxy(user, {}); + +*!* +alert(allUsers.has(user)); // false +*/!* +``` + +ご覧の通り、プロキシ後はセット `allUsers` で `user` を見つけることができません。プロキシは異なるオブジェクトだからです。 + +```warn header="プロキシは厳密等価 `===` をインターセプトすることはできません" +プロキシは `new`(`construct`), `in`(`has`), `delete`(`deleteProperty`)などの多くの演算子をインターセプトすることができます。 + +しかし、オブジェクトへの厳密等価テストをインターセプトする方法はありません。オブジェクトは自身にのみ厳密に等しく、他の値とは等しくありません。 + +したがって、オブジェクトの等価を比較するすべての演算子と組み込みのクラスはオブジェクトとプロキシを区別します。ここには透過的な替わりはありません。 +``` + +## 取り消し可能(revocable)なプロキシ + +*取り消し可能(revocable)* なプロキシは、無効にすることのできるプロキシです。 + +リソースに対して、いつでもアクセスを閉じられるようにしたいとしましょう。 + +その方法としては、リソースをトラップをしない取り消し可能なプロキシでラップすることです。このようなプロキシはオブジェクトへ操作を転送しつつ、いつでもそれを無効にすることができます。 + +構文は次の通りです: + +```js +let {proxy, revoke} = Proxy.revocable(target, handler) +``` + +この呼び出しは `proxy` と無効にするために `revoke` 関数を持つオブジェクトを返します。 + +例: + +```js run +let object = { + data: "Valuable data" +}; + +let {proxy, revoke} = Proxy.revocable(object, {}); + +// オブジェクトの代わりにプロキシをどこかに渡します +alert(proxy.data); // Valuable data + +// 後で次のようにします +revoke(); + +// すると、プロキシは機能しなくなります(無効化されました) +alert(proxy.data); // Error +``` + +`revoke()` 呼び出しは、プロキシからターゲットオブジェクトへのすべての内部参照を削除します。これにより繋がりがなくなります。 + +初期状態で、`revoke` は `proxy` とは別なので、現在のスコープに `revoke` を残したまま、`proxy` を渡すことが可能です。 + +`proxy.revoke = revoke` と設定することで、proxy に `revoke` メソッドをバインドすることもできます。 + +別の選択肢は、`WeakMap` を作成し、キーとして `proxy` を、値として対応する `revoke` をもたせることです。これで、簡単に proxy に対する `revoke` を見つけることができます。 + +```js run +*!* +let revokes = new WeakMap(); +*/!* + +let object = { + data: "Valuable data" +}; + +let {proxy, revoke} = Proxy.revocable(object, {}); + +revokes.set(proxy, revoke); + +// ..later in our code.. +revoke = revokes.get(proxy); +revoke(); + +alert(proxy.data); // Error (revoked) +``` + +ここで `Map` の代わりに `WeakMap` を使用しているのは、ガベージコレクションをブロックしないようにするためです。proxy オブジェクトが "到達不可能" になった(e.g それを参照する変数がなくなった)場合、`WeakMap` を利用すると、不要になった `revoke` を一緒にメモリ上から削除することができます。 + +## リファレンス + +- 仕様: [Proxy](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots). +- MDN: [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). + +## サマリ + +`Proxy` はオブジェクトのラッパーであり、操作をオブジェクトへ転送し、必要に応じてその一部をトラップします。 + +クラスや関数を含め、あらゆる種類のオブジェクトをラップすることができます。 + +構文: + +```js +let proxy = new Proxy(target, { + /* traps */ +}); +``` + +...それ以降はどこでも `target` の代わりに `proxy` を使う必要があります。プロキシは独自のプロパティやメソッドは持っていません。トラップが指定されていれば操作をトラップし、そうでなければ `target` オブジェクトに転送します。 + +以下をトラップすることができます: +- プロパティ(存在しないものも含む)の読み取り(`get`)、書き込み(`set`)、削除(`deleteProperty`) +- 関数呼び出し(`apply` トラップ) +- `new` 演算子(`construct` トラップ) +- その他多くのトラップ(完全なリストはこの記事の冒頭と [docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)にあります。) + +これにより、"仮想の" プロパティやメソッドを作成したり、デフォルト値、オブザーバブルオブジェクト、関数デコレータなど様々なものを実装することができます。 + +また、異なるプロキシで複数回オブジェクトをラップし、機能の様々な側面でオブジェクトデコレートすることも可能です。 + +[Reflect](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect) API は [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) を補完するためのものとして設計されています。すべての `Proxy` トラップに対して、同じ引数を持つ `Reflect` 呼び出しがあります。これらを使用してターゲットオブジェクトに転送する必要があります。 + +プロキシにはいくつか制限があります: + +- 組み込みのオブジェクトには "内部スロット" があり、それらへのアクセスはプロキシすることはできません。上記の回避策を参照してください。 +- プライベートクラスフィールドにも同じことが当てはまります。それらは内部的にはスロットを使用して実装されているため、プロキシされたメソッド呼び出しは、それらにアクセスするために `this` としてターゲットオブジェクトをもつ必要があります。 +- オブジェクトの等価評価 `===` はインターセプトできません。 +- パフォーマンス: ベンチマークはエンジンによりますが、通常、最も単純なプロキシを使用したプロパティへのアクセスするにも数倍時間がかかります。しかし実際にそれが問題になるのは一部の "ボトルネック" オブジェクトのみです。 diff --git a/1-js/99-js-misc/01-proxy/proxy-inherit-admin.svg b/1-js/99-js-misc/01-proxy/proxy-inherit-admin.svg new file mode 100644 index 0000000000..bc6c4ce2f5 --- /dev/null +++ b/1-js/99-js-misc/01-proxy/proxy-inherit-admin.svg @@ -0,0 +1 @@ +_name: "Guest" name: getter_name: "Admin"user (proxied)original useradmin[[Prototype]] \ No newline at end of file diff --git a/1-js/99-js-misc/01-proxy/proxy-inherit.svg b/1-js/99-js-misc/01-proxy/proxy-inherit.svg new file mode 100644 index 0000000000..6c34c0f4ed --- /dev/null +++ b/1-js/99-js-misc/01-proxy/proxy-inherit.svg @@ -0,0 +1 @@ +_name: "Guest" name: getteruser (proxied)original user \ No newline at end of file diff --git a/1-js/99-js-misc/01-proxy/proxy.svg b/1-js/99-js-misc/01-proxy/proxy.svg new file mode 100644 index 0000000000..157e350f44 --- /dev/null +++ b/1-js/99-js-misc/01-proxy/proxy.svg @@ -0,0 +1 @@ +test: 5proxytargetget proxy.test5 \ No newline at end of file diff --git a/1-js/99-js-misc/02-eval/1-eval-calculator/solution.md b/1-js/99-js-misc/02-eval/1-eval-calculator/solution.md new file mode 100644 index 0000000000..76644e2efb --- /dev/null +++ b/1-js/99-js-misc/02-eval/1-eval-calculator/solution.md @@ -0,0 +1,11 @@ +`eval` を使用して数学式を計算してみましょう。 + +```js demo run +let expr = prompt("Type an arithmetic expression?", '2*3+2'); + +alert( eval(expr) ); +``` + +ユーザは任意のテキストあるいはコードが入力できます。 + +これを安全にし算術のみに制限するために、[regular expression](info:regular-expressions) を使用して `expr` をチェックし、数字と演算子のみを含むようにすることができます。 diff --git a/1-js/99-js-misc/02-eval/1-eval-calculator/task.md b/1-js/99-js-misc/02-eval/1-eval-calculator/task.md new file mode 100644 index 0000000000..91e3c54b04 --- /dev/null +++ b/1-js/99-js-misc/02-eval/1-eval-calculator/task.md @@ -0,0 +1,11 @@ +importance: 4 + +--- + +# Eval-計算機 + +算術式の入力を求め、その結果を返す計算機を作成します。 + +このタスクで式の正確性を確認する必要はありません。結果を評価して返してください。 + +[demo] diff --git a/1-js/99-js-misc/02-eval/article.md b/1-js/99-js-misc/02-eval/article.md new file mode 100644 index 0000000000..33aba46f6a --- /dev/null +++ b/1-js/99-js-misc/02-eval/article.md @@ -0,0 +1,114 @@ +# Eval: コード文字列を実行する + +組み込みの `eval` 関数を使うとコード文字列を実行することができます。 + +構文: + +```js +let result = eval(code); +``` + +例: + +```js run +let code = 'alert("Hello")'; +eval(code); // Hello +``` + +コードの文字列は長かったり、改行や関数定義、変数を含んでいる可能性があります。 + +`eval` の結果は最後の文の結果です。 + +例: +```js run +let value = eval('1+1'); +alert(value); // 2 +``` + +```js run +let value = eval('let i = 0; ++i'); +alert(value); // 1 +``` + +eval されたコードは現在のレキシカル環境で実行されるため、外部変数を参照することができます。: + +```js run no-beautify +let a = 1; + +function f() { + let a = 2; + +*!* + eval('alert(a)'); // 2 +*/!* +} + +f(); +``` + +同様に外部変数を変更することもできます: + +```js untrusted refresh run +let x = 5; +eval("x = 10"); +alert(x); // 10, 値の変更 +``` + +strict モードでは、`eval` は独自のレキシカル環境を持ちます。そのため、eval 内で宣言された関数や変数は外側では見えません。: + +```js untrusted refresh run +// 実行可能な例では、'use strict' はデフォルトで有効になっています + +eval("let x = 5; function f() {}"); + +alert(typeof x); // undefined (そのような変数はありません) +// function f も見えません +``` + +`use strict` がなければ、`eval` は独自のレキシカル環境を持たないので、外側から `x` や `f`を見ることができます。 + +## "eval" を利用する + +モダンプログラミングでは、`eval` は非常に控えめに使用されます。しばしば "eval は悪" と言われます。 + +理由は簡単です: ずっと昔、JavaScript は今よりずっと弱い言語であり、多くのことは `eval` でしかできませんでした。ですが、それから 10年が経過しました。 + +現在は、`eval` を利用する理由はほとんどありません。もし誰かがそれを使用しているなら、モダンな言語構造、あるいは [JavaScript Module](info:modules)に置き換えるよい機会です。 + +外部変数にアクセスする機能には副作用があることに注意してください。 + +コードの minifier (JSを本番環境に適用する前に使われるツールで、JSを圧縮(minify)します)は最適化のために、ローカル変数をより短いものに置き換えます。これは通常安全ですが、`eval` が使われている場合、それらを参照する可能性があるため安全ではありません。したがって、minifier は `eval` から見える可能性のあるすべてのローカル変数を置き換えません。これはコードの圧縮率に悪影響を及ぼします。 + +`eval` の内部で外部のローカル変数を使用することは、コードのメンテナンスをより難しくするためバッドプラクティスとされています。 + +このような問題に対し、完全に安全にする方法は2つです。 + +**eval されたコードが外部変数を使用していない場合、`eval` を `window.eval(...)` で呼び出してください** + +この方法では、コードはグローバルスコープで実行されます。: + +```js untrusted refresh run +let x = 1; +{ + let x = 5; + window.eval('alert(x)'); // 1 (グローバル変数) +} +``` + +**eval されたコードがローカル変数を必要とする場合、`eval` を `new Function` に変更し、引数としてそれらを渡してください** + +```js run +let f = new Function('a', 'alert(a)'); + +f(5); // 5 +``` + +`new Function` についてはチャプター で説明しています。これは文字列から関数を作成し、グローバルスコープになります。そのため、ローカル変数は見えません。ですが、上記の例のように、引数として明示的に渡すほうがはるかに明白です。 + +## サマリ + +`eval(code)` の呼び出しはコード文字列を実行し、最後の文の結果を返します。 +- 通常は必要ないため、モダン JavaScript ではめったに使われません。 +- 外部のローカル変数にアクセスできます。これはバッドプラクティスとされています。 +- 代わりに、グローバルスコープでコードを `eval` するには `window.eval(code)` を使います。 +- あるいは、外部スコープから何らかのデータが必要な場合は `new Function` を使い引数としてそれを渡します。 diff --git a/1-js/06-advanced-functions/11-currying-partials/1-ask-currying/solution.md b/1-js/99-js-misc/03-currying-partials/1-ask-currying/solution.md similarity index 100% rename from 1-js/06-advanced-functions/11-currying-partials/1-ask-currying/solution.md rename to 1-js/99-js-misc/03-currying-partials/1-ask-currying/solution.md diff --git a/1-js/99-js-misc/03-currying-partials/1-ask-currying/task.md b/1-js/99-js-misc/03-currying-partials/1-ask-currying/task.md new file mode 100644 index 0000000000..e3ccbba43c --- /dev/null +++ b/1-js/99-js-misc/03-currying-partials/1-ask-currying/task.md @@ -0,0 +1,33 @@ +importance: 5 + +--- + +# ログイン用の部分的なアプリケーション + +このタスクは 少しより複雑なバリアントです。 + +`user` オブジェクトが修正されました。今、2つの関数 `loginOk/loginFail` の代わりに、単一の関数 `user.login(true/false)` があります。 + +下のコードでは、何を渡すと `ok` として `user.login(true)` を、`fail` として `user.login(fail)` を呼ぶでしょうか? + +```js +function askPassword(ok, fail) { + let password = prompt("Password?", ''); + if (password == "rockstar") ok(); + else fail(); +} + +let user = { + name: 'John', + + login(result) { + alert( this.name + (result ? ' logged in' : ' failed to log in') ); + } +}; + +*!* +askPassword(?, ?); // ? +*/!* +``` + +変更はハイライトされた箇所の修正だけにしてください。 diff --git a/1-js/99-js-misc/03-currying-partials/article.md b/1-js/99-js-misc/03-currying-partials/article.md new file mode 100644 index 0000000000..38debcf329 --- /dev/null +++ b/1-js/99-js-misc/03-currying-partials/article.md @@ -0,0 +1,180 @@ +libs: + - lodash + +--- + +# カリー化 + +[カリー化](https://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%AA%E3%83%BC%E5%8C%96)は、関数を扱う際の上級テクニックです。これはJavaScriptだけでなく、他の言語でも使用されます。 + +カリー化は`f(a, b, c)`として呼び出せる関数を`f(a)(b)(c)`のように呼び出せるようにする、関数の変形のことを指します。 + +カリー化は関数を呼び出しません。ただ変形するだけです。 + +まず例を確認して、何について話しているかもっと理解して、それから実用的な応用例を見てみましょう。 + +2引数を取る関数`f`をカリー化する、ヘルパー関数の`curry(f)`を作成します。言い換えると、2引数の`f(a, b)`に対して`curry(f)`は、関数を`f(a)(b)`のように呼び出せるように変形します。 + +```js run +*!* +function curry(f) { // curry(f)によって変形が施されます + return function(a) { + return function(b) { + return f(a, b); + }; + }; +} +*/!* +// 使用法 +function sum(a, b) { + return a + b; +} +let curriedSum = curry(sum); +alert( curriedSum(1)(2) ); // 3 +``` + +ご覧の通り、実装は単純です:これはただの2つのラッパです。 + +- `curry(func)`は`function(a)`のラッパです。 +- これが`curriedSum(1)`のように呼ばれると、引数はレキシカル環境に保存され、新しいラッパである`function(b)`が返されます。 +- そしてこのラッパが`2`を引数として呼ばれると、元の`sum`への呼び出しを返します。 + +例えばloadashライブラリの[_.curry](https://lodash.com/docs#curry)のような、もっと高度なカリー化の実装では、関数を通常通り呼んだり部分的に呼んだりすることが認められるようなラッパが返されます。 + +```js run +function sum(a, b) { + return a + b; +} +let curriedSum = _.curry(sum); // loadashライブラリの _.curry を使用します +alert( curriedSum(1, 2) ); // 3, 通常通り呼び出します +alert( curriedSum(1)(2) ); // 3, 部分的に呼び出します +``` + +## カリー化?なんのために? + +利便性を理解するためには、価値のある実例が必要です。 + +例えば、情報を整形して出力する`log(date, importance, message)`というロギング関数があります。実際のプロジェクトでは、そのような関数はネットワーク越しにログを送信するなどの多様で便利な機能を持っていますが、ここでは単純に`alert`を使用します。 + +```js run +function log(date, importance, message) { + alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`); +} +``` + +これをカリー化しましょう! + +```js +log = _.curry(log); +``` + +そうすると、`log`は通常通り機能します: + +```js +log(new Date(), "DEBUG", "some debug"); // log(a, b, c) +``` + +……そしてカリー化後の形式でも機能します: + +```js +log(new Date())("DEBUG")("some debug"); // log(a)(b)(c) +``` + +すると、現在のログ用の便利な関数を簡単に作成できます: + +```js +// logNowはlogの部分関数で、第1引数が固定されています +let logNow = log(new Date()); + +// それを使用します +logNow("INFO", "message"); // [HH:mm] INFO message +``` + +これで、`logNow`は第1引数が固定された`log`となりました。言い換えれば「部分適用された関数」あるいは短くすると「部分適用」となります。 + +さらに進んで、現在のデバッグログ用の便利な関数を作成することもできます: + +```js +let debugNow = logNow("DEBUG"); +debugNow("message"); // [HH:mm] DEBUG message +``` + +つまり: +1. カリー化しても何も失いません:`log`を通常通り呼び出すこともできます。 +2. 今日のログ用のような部分適用された関数を簡単に生成することができます。 + +## 高度なカリー化の実装 + +もし詳細に触れたいのならば、こちらが上記でも使用できた、複数の引数を取る関数用の「高度な」カリー化の実装です。 + +かなり短いです: + +```js +function curry(func) { + return function curried(...args) { + if (args.length >= func.length) { + return func.apply(this, args); + } else { + return function(...args2) { + return curried.apply(this, args.concat(args2)); + } + } + }; +} +``` + +使用例: + +```js +function sum(a, b, c) { + return a + b + c; +} +let curriedSum = curry(sum); +alert( curriedSum(1, 2, 3) ); // 6, 普通に呼び出すことも可能です +alert( curriedSum(1)(2,3) ); // 6, 最初の引数をカリー化しています +alert( curriedSum(1)(2)(3) ); // 6, 完全なカリー化です +``` + +新しい`curry`は複雑なように見えますが、実際は簡単に理解できます。 + +`curry(func)`呼び出しの結果は、ラップされた以下のような`curried`関数です。 + +```js +// funcは変形対象の関数です +function curried(...args) { + if (args.length >= func.length) { // (1) + return func.apply(this, args); + } else { + return function(...args2) { // (2) + return curried.apply(this, args.concat(args2)); + } + } +}; +``` + +これを実行すると、2つの`if`の分岐に出会います: + +1. もし渡された`args`の数が、元の関数で定義されている引数の数以上であれば(`func.length`)、単に`func.apply`を使用して関数を呼び出します。 +2. そうでなければ、部分適用します:`func`を単には呼び出しません。その代わりに、別のラッパが返されます。そのラッパは新しい引数とともに、以前与えられた引数を`curried`に再び適用します。 + +それから繰り返しになりますが、`curried`を呼び出せば、新しい部分適用(引数の数が十分ではない場合)か、最終的には結果が得られます。 + +```smart header="固定長の関数のみ" +カリー化では、関数の引数の数は固定されている必要があります。 + +`f(...args)`のような、残りのパラメータを使用するような関数は、このようにはカリー化できません。 +``` + +```smart header="カリー化よりもさらに" +定義上、カリー化は sum(a, b, c) を sum(a)(b)(c) に変換するべきです。 + +しかし、JavaScriptでのカリー化のほとんどの実装は説明されているように高度であり、複数引数のバリアントでも関数が呼び出し可能となっています。 +``` + +## サマリ + + + +*カリー化*は`f(a,b,c)`を`f(a)(b)(c)`として呼び出し可能に変換します。JavaScriptの実装は、通常の形で呼び出し可能な関数を維持し、かつ引数が不足している場合には部分適用を返します。 + +簡単な部分適用がほしいときにカリー化は素晴らしいです。ロギングの例で見てきたように、カリー化後の3引数を取る汎用的な関数`log(date, importance, message)`は、(`log(date)`のような)1つの引数または(`log(date, importance)`のような)2つの引数で呼び出された時には部分適用を返します。 diff --git a/1-js/04-object-basics/04-object-methods/2-check-syntax/solution.md b/1-js/99-js-misc/04-reference-type/2-check-syntax/solution.md similarity index 100% rename from 1-js/04-object-basics/04-object-methods/2-check-syntax/solution.md rename to 1-js/99-js-misc/04-reference-type/2-check-syntax/solution.md diff --git a/1-js/99-js-misc/04-reference-type/2-check-syntax/task.md b/1-js/99-js-misc/04-reference-type/2-check-syntax/task.md new file mode 100644 index 0000000000..1afed12215 --- /dev/null +++ b/1-js/99-js-misc/04-reference-type/2-check-syntax/task.md @@ -0,0 +1,19 @@ +importance: 2 + +--- + +# 構文チェック + +このコードの結果はなんでしょう? + + +```js no-beautify +let user = { + name: "John", + go: function() { alert(this.name) } +} + +(user.go)() +``` + +P.S. 落とし穴があります :) diff --git a/1-js/99-js-misc/04-reference-type/3-why-this/solution.md b/1-js/99-js-misc/04-reference-type/3-why-this/solution.md new file mode 100644 index 0000000000..e85d2ff505 --- /dev/null +++ b/1-js/99-js-misc/04-reference-type/3-why-this/solution.md @@ -0,0 +1,21 @@ + +説明します。 + +1. これは通常のオブジェクトメソッド呼び出しです。 + +2. 同じです。ここでは括弧は操作の順番を変更しません。ドットが最初です。 + +3. ここにより複雑な呼び出し `(expression).method()` があります。この呼出しはまるで2行に分割されたかのようにして動作します。: + + ```js no-beautify + f = obj.go; // 式を計算します。 + f(); // 持っているものを実行します + ``` + + ここで、`f()` は `this` なしの関数として実行されます。 + +4. `(3)` と似たようなもので、ドット `.` の左側に式を持っています。 + +`(3)` と `(4)` の振る舞いを説明するために、プロパティ・アクセサ(ドットまたは角括弧)が参照型の値を返すことを思い出す必要があります。 + +メソッド呼び出し(代入 `=`や `||`のような)以外の操作は、 `this` に設定できる情報を持たない通常の値にします。 diff --git a/1-js/04-object-basics/04-object-methods/3-why-this/task.md b/1-js/99-js-misc/04-reference-type/3-why-this/task.md similarity index 100% rename from 1-js/04-object-basics/04-object-methods/3-why-this/task.md rename to 1-js/99-js-misc/04-reference-type/3-why-this/task.md diff --git a/1-js/99-js-misc/04-reference-type/article.md b/1-js/99-js-misc/04-reference-type/article.md new file mode 100644 index 0000000000..7c3c8b947e --- /dev/null +++ b/1-js/99-js-misc/04-reference-type/article.md @@ -0,0 +1,106 @@ + +# 参照型 + +```warn header="詳細な言語機能" +このセクションでは、特殊なケースをより理解するための高度なトピックについて説明します。 + +あなたが速く読み進めたいのであれば、スキップまたは別の機会に見てください。 +``` + +複雑なメソッド呼び出しは、 `this` を失う可能性があります。例えば: + +```js run +let user = { + name: "John", + hi() { alert(this.name); }, + bye() { alert("Bye"); } +}; + +user.hi(); // John (シンプルな呼び出しは動作します) + +*!* +// 今、name に応じて user.hi または user.bye を読んでみましょう +(user.name == "John" ? user.hi : user.bye)(); // Error! +*/!* +``` + +最後の行では、`user.hi` か `user.bye` を選択する三項演算子があります。このケースでは、結果は `user.hi` です。 + +メソッドは丸括弧 `()` ですぐに呼び出されます。しかし、それは正しく動きません! + +呼び出しはエラーになります、なぜなら、呼び出しの内側の `"this"` の値は `undefined` になるからです。 + +これは動きます (オブジェクトドットメソッド): +```js +user.hi(); +``` + +これはダメです (評価されたメソッド): +```js +(user.name == "John" ? user.hi : user.bye)(); // Error! +``` + +なぜでしょう?なぜそのようなことが起こるのか理解したい場合、`obj.method()` の呼び出しがどのように機能するのかを理解してみましょう。 + +## 参照型の説明 + +よく見ると、 `obj.method()` 文に2つの操作があります: + +1. まず、ドット `'.'` がプロパティ `obj.method` を抽出します。 +2. 次に、丸括弧 `()` でそれを実行します。 + +そして、`this` についての情報は最初の処理から2つ目の処理へどのように渡されるでしょう? + +それらの操作を別々の行に書いた場合、`this` が失われるのは明らかでしょう: + +```js run +let user = { + name: "John", + hi() { alert(this.name); } +} + +*!* +// メソッドの取得呼び出しを2行に分けます +let hi = user.hi; +hi(); // Error, this は undefined なので +*/!* +``` + +ここで `hi = user.hi` は関数を変数の中においています。そして最後の行は完全に独立しています。なので、`this` がありません。 + +**`user.hi()` 呼び出しを動作させるために、JavaScriptはトリックを使います -- ドット `'.'` は関数ではなく、特別な[参照型](https://tc39.github.io/ecma262/#sec-reference-specification-type)を返します。** + +参照型は "仕様上の型" です。私たちは明示的にそれを使うことはできませんが、言語の中で内部的に使われています。 + +参照型の値は、3つの値の組み合わせ `(base, name, strict)` です。ここで: + +- `base` はオブジェクトです。 +- `name` はプロパティです。 +- `strict` は `use strict` が効いている場合は true です。 + +`user.hi` へのプロパティアクセスの結果は、関数ではなく参照型です。strict mode での `user.hi` はこうなります: + +```js +// 参照型の値 +(user, "hi", true) +``` + +参照型に対して丸括弧 `()` 呼び出しがされると、それらはオブジェクトとそのメソッドについての完全な情報を受け取り、正しい `this` (このケースでは `user`)をセットできます。 + +参照型はドット `.` から呼び出し括弧 `()` へ情報を渡す目的の特別な "中間" の内部型です。 + +代入 `hi = user.hi` のような他の操作は、参照型を破棄し、`user.hi`(関数)の値を渡します。従って、それ以降の操作は全て `this` を "失います"。 + +なので、結果として、`this` の値は、関数がドット `obj.method()`、もしくは角括弧 `obj[method]()`構文を使って直接呼び出された場合のみ正しく渡されます。このチュートリアルの後半では、[func.bind()](/bind#solution-2-bind) など、この問題を解決するためのさまざまな方法を学びます。 + +## サマリ + +参照型は言語の内部の型です。 + +`obj.method()` 内の `.` のようなプロパティの読み取りでは、正確なプロパティ値ではなくプロパティ値とそれが取得されたオブジェクトの両方を保持する特別な "参照型" の値を返します。 + +これはその後に続くメソッド呼び出し `()` がオブジェクトを取得しそこに `this` を設定するためです。 + +その他すべての操作では、参照型は自動的にプロパティ値になります(上のケースでは関数)。 + +このメカニズム全体は我々の目からは見えません。式を使用して、メソッドがオブジェクトから動的に取得される場合など、微妙なケースでのみ問題になります。 diff --git a/1-js/99-js-misc/05-bigint/article.md b/1-js/99-js-misc/05-bigint/article.md new file mode 100644 index 0000000000..8f2babbed6 --- /dev/null +++ b/1-js/99-js-misc/05-bigint/article.md @@ -0,0 +1,130 @@ +# BigInt + +[recent caniuse="bigint"] + +`BigInt` は任意の長さの整数をサポートする特別な数値型です。 + +bigint は `n` を整数リテラルの末尾に追加するか、文字列や数字などから bigint を作成する関数 `BigInt` を呼び出すことによって生成されます。 + +```js +const bigint = 1234567890123456789012345678901234567890n; + +const sameBigint = BigInt("1234567890123456789012345678901234567890"); + +const bigintFromNumber = BigInt(10); // 10n と同じ +``` + +## 算術演算子 + +`BigInt` はほぼ通常の数値のように扱うことができます。例えば: + +```js run +alert(1n + 2n); // 3 + +alert(5n / 2n); // 2 +``` + +注意: 除算 `5/2` は小数部分なしでゼロに向かって丸められた結果を返します。すべての bigint に対する操作は bigint を返します。 + +bigint と通常の数値を混在させることはできません: + +```js run +alert(1n + 2); // Error: BigInt と他の型を混ぜることはできません +``` + +必要なら明示的な変換が必要です: 次のように `BigInt()` か `Number()` を利用します: + +```js run +let bigint = 1n; +let number = 2; + +// number から bigint へ +alert(bigint + BigInt(number)); // 3 + +// bigint から number へ +alert(Number(bigint) + number); // 3 +``` + +変換操作は常に粛々と行われ、決してエラーになりません。が、bigint が大きすぎて数値型にフィットしない場合、余分なビットが切り捨てられるため、このような変換には注意が必要です。 + +````smart header="bigint では単項プラスはサポートされていません。" +単項プラス演算子 `+value` は `value` を数値に変換するためのよく知られた方法です。 + +混乱を避けるため、bigint ではサポートされていません: +```js run +let bigint = 1n; + +alert( +bigint ); // error +``` +したがって、bigint を数値に変換する場合には `Number()` を使用してください。 +```` + +## 比較 + +`<`, `>` のような比較は bigint と数値でうまく機能します: + +```js run +alert( 2n > 1n ); // true + +alert( 2n > 1 ); // true +``` + +ですが注意してください。数値と bigint は異なる型なので、等価 `==` にはなりますが、厳密等価 `===` にはなりません。 + +```js run +alert( 1 == 1n ); // true + +alert( 1 === 1n ); // false +``` + +## Boolean 操作 + +`if` や他の boolean 操作内では、bigint は数値のように振る舞います。 + +例えば、`if` では bigint `0n` は偽であり、他の値は真です: + +```js run +if (0n) { + // 実行されることはありません +} +``` + +bigint を利用した `||`, `&&` などの boolean 操作も数値と同じように動作します: + +```js run +alert( 1n || 2 ); // 1 (1n は真とみなされます) + +alert( 0n || 2 ); // 2 (0n は偽とみなされます) +``` + +## Polyfills + +bigint のポリフィルには注意が必要です。理由は `+`, `-` などの多くの JavaScript 演算子は通常の数値の場合と比較して、bigint での動作は異なるためです。 + +例えば、bigint の除算は常に bigint を返します(必要に応じて丸められます)。 + +このような振る舞いをエミュレートするために、ポリフィルはコードを分析し、そのようなすべての演算子をその関数に置き換える必要があります。ですが、それをするのは面倒であり、多くのパフォーマンスコストがかかります。 + +そのため、よく知られている、良いポリフィルはありません。 + +ですが、その逆は [JSBI](https://github.com/GoogleChromeLabs/jsbi) ライブラリの開発者により提案されています。 + +このライブラリは独自のメソッドを使用して大きな数字を実装しています。我々は、ネイティブの bigint の代わりにこれが利用できます: + +| 操作 | ネイティブ `BigInt` | JSBI | +|-----------|-----------------|------| +| 数値から作成 | `a = BigInt(789)` | `a = JSBI.BigInt(789)` | +| 足し算 | `c = a + b` | `c = JSBI.add(a, b)` | +| 引き算 | `c = a - b` | `c = JSBI.subtract(a, b)` | +| ... | ... | ... | + +...そしてポリフィル(Babelプラグイン)を使用して、JSBI呼び出しをブラウザがサポートしているネイティブ bigint に変換します。 + +つまり、このアプローチはネイティブの bigint の代わりに JSBI でコードを書くことを提案しています。しかし、JSBI は内部的には bigint と同じように数値を処理し、仕様にしがたってエミュレートするので、このコードは "bigint対応" になります。 + +このようなJSBIコードは、bigintをサポートしていないエンジンとサポートしているエンジンに "そのまま" 使用できます。ポリフィルは、呼び出しをネイティブbigintに変換します。 + +## 参照 + +- [MDN docs on BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt). +- [Specification](https://tc39.es/ecma262/#sec-bigint-objects). diff --git a/1-js/99-js-misc/index.md b/1-js/99-js-misc/index.md new file mode 100644 index 0000000000..0d27b0c77c --- /dev/null +++ b/1-js/99-js-misc/index.md @@ -0,0 +1,2 @@ + +# その他 diff --git a/2-ui/1-document/01-browser-environment/article.md b/2-ui/1-document/01-browser-environment/article.md index 9eec6e3d47..3bfab8aa40 100644 --- a/2-ui/1-document/01-browser-environment/article.md +++ b/2-ui/1-document/01-browser-environment/article.md @@ -1,19 +1,19 @@ -# ブラウザ環境, 仕様 +# ブラウザ環境, スペック 当初、JavaScript言語は web ブラウザのために作られました。それ以降、言語は進化し、多くの用途やプラットフォームをもつ言語になりました。 -ホスト環境は、言語のコアに加えてプラットフォーム固有のオブジェクトや機能を提供します。Web ブラウザは webページを制御する手段を提供します。Node.JSはサーバサイドの機能などを提供します。 +プラットフォームは、ブラウザ、Webサーバ、あるいは別の *ホスト*、JavaScript が実行可能であれば "スマートな" コーヒーマシンかもしれません。これらはプラットフォーム固有の機能を提供します。JavaScript スペックではこれを *ホスト環境* と呼んでいます。 -[cut] +ホスト環境は言語のコアに加えて、独自のオブジェクトや機能を提供します。Webブラウザであれば Webページを制御する手段を、Node.js であればサーバサイドの機能などです。 これは、JavaScript がWebブラウザで実行されているときの鳥瞰図です: -![](windowObjects.png) +![](windowObjects.svg) -`window` と呼ばれる "ルート" オブジェクトがあります。それは2つの役割を持ちます。: +`window` と呼ばれる "ルート" オブジェクトがあります。これは2つの役割を持ちます。: -1. 1つ目は、それはJavaScriptコードのグローバルオブジェクトであり、チャプター で説明したとおりです。 -2. 2つ目は、それは "ブラウザウィンドウ" を表し、ウィンドウを制御するためのメソッドを提供します。 +1. 1つ目は、これはJavaScriptコードのグローバルオブジェクトであり、 の章で説明するとおりです。 +2. 2つ目は、これは "ブラウザウィンドウ" を表し、ウィンドウを制御するためのメソッドを提供します。 例えば、ここではグローバルオブジェクトとして使います: @@ -32,46 +32,35 @@ window.sayHi(); alert(window.innerHeight); // 内部の window の高さ ``` -window固有のメソッドやプロパティがたくさんあります。我々は後ほどそれをカバーしましょう。 +window固有のメソッドやプロパティはたくさんあります。後ほどそれらを見ていきます。 -## ドキュメントオブジェクトモデル (DOM) +## DOM(ドキュメントオブジェクトモデル) -`document` オブジェクトはページのコンテンツへのアクセスを提供します。私たちはそれを使ってページ上のものを変更したり作成することができます。 +ドキュメントオブジェクトモデル、略して DOM は、ページ全体のコンテンツを変更可能なオブジェクトとして表現します。 + +`document` オブジェクトはページのメインの "エントリーポイント" です。これを使って、ページ上のものを変更したり作成することができます。 例: ```js run // 背景色を赤に変える document.body.style.background = 'red'; -// それを1秒後に戻す +// 1秒後に戻す setTimeout(() => document.body.style.background = '', 1000); ``` -ここでは `document.body.style` を使いましたが、まだまだあります。プロパティとメソッドは仕様で説明されています。偶然にも、それを開発する2つのワーキンググループがあります: - -1. [W3C](https://en.wikipedia.org/wiki/World_Wide_Web_Consortium) -- ドキュメントは です. -2. [WhatWG](https://en.wikipedia.org/wiki/WHATWG), で公開しています. - -あいにく、2つのグループが必ずしも一致するわけではないので、2組の標準があります。しかし、彼らは密接な関係にあり、最終的には物事はマージされます。従って、あなたが見つけたドキュメントはとても似ており、99% 一致します。ごくわずかな違いがありますが、気づかないかもしれません。 - -個人的には、 が使いやすいと思います。 - -ずいぶん昔、まったく標準がありませんでした -- 各ブラウザが必要なものを実装しました。従って、異なるブラウザは、同じものに対して異なるセットのメソッドとプロパティを持っていました。そして、開発者はそれぞれのために異なるコードを書かなければなりませんでした。暗く、散らかった時代です。 - -今でさえ、ブラウザ固有のプロパティを使い、非互換を回避している古いコードに出会うことがあります。しかし、このチュートリアルでは、現代の物を使います: あなたが本当に必要になるときまで、古いものを学ぶ必要はありません(本当に必要になるときはあまりないでしょう)。 - -その後、誰もが合意をするための試みとして、DOM標準が現れました。最初のバージョンは "DOM Level 1" で、それは DOM Level 2, 次に DOM Level 3 と拡張され、今は DOM Level 4 になりました。WhatWG グループの人々はバージョンに疲れて、単に "DOM" と番号なしで呼んでいます。私たちもそうしましょう。 +ここでは `document.body.style` を使いましたが、まだまだあります。プロパティとメソッドはスペック : [DOM Living Standard](https://dom.spec.whatwg.org) で説明されています。 ```smart header="DOM はブラウザだけではありません" -DOM 仕様は ドキュメント の構造を説明し、それを操作するためのオブジェクトを提供します。それを使う非ブラウザのものもあります。 +DOM スペックはドキュメントの構造を説明し、操作するためのオブジェクトを提供します。DOM を使う非ブラウザのものもあります。 -例えば、HTMLページをダウンロードしそれを処理するサーバサイドのツールです。DOM仕様の一部のみをサポートしているかもしれませんが。 +例えば、HTMLページをダウンロードしそれを処理するサーバサイドのスクリプトです。DOM スペックの一部のみをサポートしているかもしれません。 ``` ```smart header="スタイルのための CSSOM" -CSS ルールやスタイルシートは HTML のように構造化されていません。そのため、それらがオブジェクトとしてどのように表現され、どのようにそれらを読み書きするかを説明する別の仕様[CSSOM](https://www.w3.org/TR/cssom-1/)があります。 +CSS ルールやスタイルシートのための別のスペック[CSS オブジェクトモデル(CSSOM)](https://www.w3.org/TR/cssom-1/)があります。 これはオブジェクトとしてどのように表現され、どのようにそれらを読み書きするかが記述されています。 -CSSMON は document のスタイルルールを変更するとき、DOM と一緒に使われます。実際には、通常はCSSルールが静的であるため、CSSOMはめったに必要ありません。 JavaScriptのCSSルールを追加/削除することはめったにありませんので、今すぐはカバーしません。 +CSSOM は document のスタイルルールを変更するとき、DOM と一緒に使われます。ですが実際には、JavaScript からCSSルールを変更することはめったにないため、CSSOM はほとんど使われません(通常は CSSルールの変更ではなく、CSSクラスの追加削除をします)が、それも可能です。 ``` ## BOM (HTML仕様の一部) @@ -94,27 +83,31 @@ if (confirm("Go to wikipedia?")) { 関数 `alert/confirm/prompt` もまた BOM の一部です: それらは直接は document と関係はしませんが、ユーザとコミュニケーションをとる純粋なブラウザメソッドを表します。 -```smart header="HTML 仕様" -BOM は一般的な [HTML 仕様](https://html.spec.whatwg.org)の一部です。 +```smart header="HTML スペック" +BOM は一般的な [HTML スペック](https://html.spec.whatwg.org)の一部です。 - のHTML仕様は "HTML言語" (タグ、属性) についてだけでなく、多くのオブジェクトやメソッド、ブラウザ固有のDOM拡張をカバーします。それは "広義のHTML" です。 + のHTMLスペックは "HTML言語" (タグ、属性) についてだけでなく、多くのオブジェクトやメソッド、ブラウザ固有のDOM拡張をカバーします。それは "広義のHTML" です。また、いくつかのパートは にリストされている追加のスペックがあります。 ``` ## サマリ -標準に関して: +標準に関して説明しました: -DOM 仕様 +DOM スペック : document 構造、操作、およびイベントについて説明します。 を見てください。 -CSSOM 仕様 +CSSOM スペック : スタイルシートとスタイルルール、それらの操作や document へのバインディングについて説明します。 を見てください。 -HTML 仕様 +HTML スペック : HTML言語(タグなど) や BOM(ブラウザオブジェクトモデル) を説明します -- 様々なブラウザ関数があります: `setTimeout`, `alert`, `location` など、 を見てください。それは DOM 仕様と多くの追加プロパティやメソッドを使ったその拡張です。 -document はUIで中心的な役割を果たすため、今からDOMを学ぶことになるでしょう。それを使って作業するのが最も複雑な部分です。 +加えて、いくつかのクラスは で個別に説明があります。 + +これらのリンクをメモしておいてください。学ぶことがたくさんあるので、すべてをカバーして覚えるのは不可能です。 + +プロパティまたはメソッドについて読みたくなったとき、 にある Mozilla のマニュアルも優れたリソースですが、対応するスペックを読む方がよいかもしれません: 複雑で長いかもしれませんが、基本の知識は健全で完全なものになります。 -学ぶことが非常に多くあるので、上のリンクには注意してください。すべてをカバーし、覚えるのは不可能です。 +何かを見つけるには、インターネット検索で "WHATWG [term]" あるいは "MDN [term]" を使用すると便利なことがよくあります。例: , 。 -プロパティまたはメソッドについて読みたくなったとき -- のMozilla マニュアルは良いです、が対応する仕様を読む方がよりよりかもしれません: より複雑で長いですが、基本の知識は健全で完全なものになります。 +次に、ドキュメントが UI の中心的な役割を果たすので、DOM の学習を勧めていきます。 diff --git a/2-ui/1-document/01-browser-environment/windowObjects.png b/2-ui/1-document/01-browser-environment/windowObjects.png deleted file mode 100644 index 81803bd09d..0000000000 Binary files a/2-ui/1-document/01-browser-environment/windowObjects.png and /dev/null differ diff --git a/2-ui/1-document/01-browser-environment/windowObjects.svg b/2-ui/1-document/01-browser-environment/windowObjects.svg new file mode 100644 index 0000000000..b7e18bb347 --- /dev/null +++ b/2-ui/1-document/01-browser-environment/windowObjects.svg @@ -0,0 +1 @@ +windowdocumentObjectnavigatorscreenlocationframeshistoryArrayFunctionXMLHttpRequestBOMJavaScriptDOM \ No newline at end of file diff --git a/2-ui/1-document/01-browser-environment/windowObjects@2x.png b/2-ui/1-document/01-browser-environment/windowObjects@2x.png deleted file mode 100644 index e6dae7c3d4..0000000000 Binary files a/2-ui/1-document/01-browser-environment/windowObjects@2x.png and /dev/null differ diff --git a/2-ui/1-document/02-dom-nodes/article.md b/2-ui/1-document/02-dom-nodes/article.md index e1afd450b6..a0c32ae971 100644 --- a/2-ui/1-document/02-dom-nodes/article.md +++ b/2-ui/1-document/02-dom-nodes/article.md @@ -6,13 +6,29 @@ libs: # DOM ツリー -HTML文書のバックボーンはタグです。 +HTMLドキュメントのバックボーンはタグです。 -ドキュメントオブジェクトモデル(DOM)によると、すべてのHTMLタグはオブジェクトです。入れ子のタグはそれを囲タグの "子" と呼ばれます。 +ドキュメントオブジェクトモデル(DOM)によれば、すべてのHTMLタグはオブジェクトです。入れ子のタグはそれを囲った "子" と呼ばれます。タグの内側のテキストも同様にオブジェクトです。 -タグの内側のテキストも同様にオブジェクトです。 +これらすべてのオブジェクトは JavaScript でアクセス可能であり、これらを使用してページが変更できます。 -すべてのそれらのオブジェクトは JavaScript でアクセス可能です。 +例えば、`document.body` は `` タグを表すオブジェクトです。 + +以下のコードを実行すると、3秒間 `` が赤になります: + +```js run +document.body.style.background = 'red'; // 背景を赤に変更 + +setTimeout(() => document.body.style.background = '', 3000); // 戻します +``` + +ここでは、`document.body` の背景色を変更するのに、`style.background` を使用していますが、以下のように他に多くのプロパティがあります: + +- `innerHTML` -- ノードの HTML コンテンツ +- `offsetWidth` -- ノードの幅(ピクセル) +- など + +後ほど、DOMを操作するより多くの方法を学んでいきますが、最初に知っておく必要があるのは、その構造です。 ## DOM の例 @@ -41,12 +57,14 @@ drawHtmlTree(node1, 'div.domtree', 690, 320); ```online -上の図では、要素をクリックしその子を開いたり閉じることができます。 +上の図では、要素をクリックすることで子要素を開閉できます。 ``` -タグは "要素ノード" (もしくは単に要素) と呼ばれます。入れ子のタグは囲まれたタグの子になります。結果的に、我々は要素のツリーを持ちます: `` はルートで、次に ``, `` はその子などとなります。 +すべてのツリーノードはオブジェクトです。 -要素内のテキストは *テキストノード* を形成し、 `#text` とラベル付けされます。テキストノードは文字列のみを含みます。それは子を持たず、常にツリーの葉です。 +タグは *"要素ノード"* (もしくは単に要素) と呼ばれ、ツリー構造を形成します: `` がルートで、``, `` がその子、と言った構造です。 + +要素内のテキストは *テキストノード* を形成し、 `#text` とラベル付けされます。テキストノードは文字列だけを含み、子を持たず、常にツリーの葉になります。 例えば、`` タグはテキスト `"About elks"` を持っています。 @@ -55,13 +73,13 @@ drawHtmlTree(node1, 'div.domtree', 690, 320); - 改行: `↵` (JavaScript では `\n` として知られています) - スペース: `␣` -スペースと改行 -- は完全に有効な文字で、それらはテキストノードを形成し、DOMの一部になります。従って、例えば、上の例では `<head>` タグは `<title>` の前にいくつかのスペースを含まれており、そのテキストは `#text` ノードになります(それは改行といくつかのスペースのみが含まれています)。 +スペースと改行は文字や数字と同様、完全に有効な文字です。これらはテキストノードを形成し、DOMの一部になります。従って、例えば、上の例では `<head>` タグは `<title>` の前にいくつかのスペースを含まれており、そのテキストは `#text` ノードになります(改行といくつかのスペースのみが含まれています)。 そこには2つだけ、トップレベルの除外があります: 1. `<head>` の前のスペースと改行は歴史的な理由から無視されます。 -2. もし `</body>` の後に何かをおいた場合、HTML仕様はすべてのコンテンツが `<body>` の内側でなければならないため、最後にそれらは自動的に `body` の中に移動されます。従って、`</body>` の後にスペースはないことがあります。 +2. HTMLスペックではすべてのコンテンツが `<body>` の内側でなければならないため、`</body>` の後に何かをおいた場合、最後にそれらは自動的に `body` の中に移動されます。従って、`</body>` の後にスペースはないことがあります。 -別のケースでは、すべてが正直で -- ドキュメント内にスペース(単に任意の文字のように)があれば、それらはDOMのテキストノードになります。もしそれらを削除すれば、何も存在しません。 +その他のケースはすべてが明快で、ドキュメント内にスペース(単に任意の文字のように)があれば、それらはDOMのテキストノードになります。もしそれらを削除すれば、何も存在しません。 これはスペースがないテキストノードです: @@ -79,22 +97,22 @@ drawHtmlTree(node2, 'div.domtree', 690, 210); </script> ```smart header="端のスペースとその間にある空のテキストは、通常はツール内に隠されています" -DOMを使って動作するブラウザツール(間もなくカバーされる)は、通常、テキストの最初/最後のスペースを表示せず、またタグ間に空のテキストノード(改行)も表示しません。 +DOMを使って動作するブラウザツール(この後説明します)は、通常、テキストの最初/最後のスペースを表示せず、またタグ間に空のテキストノード(改行)も表示しません。 これは、主にHTMLを装飾するために使用され、どのように表示されるかに(ほとんどの場合)影響を与えないからです。 さらにDOMの図では、物事を短く保つために、それらが無関係な場所で省略することがあります。 ``` - ## 自動補正 -もしブラウザが不正な形式のHTMLに遭遇したとき、ブラウザはDOMを作成時にそれを自動補正します。 +ブラウザが不正な形式のHTMLに遭遇した場合、DOM作成時に自動補正します。 -例えば、トップタグは常に `<html>` です。たとえドキュメントの中に存在していなくても -- それはDOMの中におり、ブラウザはそれを生成します。`<body>` についても同じです。 +例えば、トップのタグは常に `<html>` です。ブラウザは `<html>` を作成するので、たとえドキュメントの中になくても、DOMの中に存在することになります。`<body>` についても同じです。 例として、もしHTMLファイルが `"Hello"` という言葉のみだった場合、ブラウザはそれを `<html>` と `<body>` でラップし、必須の `<head>` を追加し、DOMは次のようになります: + <div class="domtree"></div> <script> @@ -125,7 +143,7 @@ drawHtmlTree(node4, 'div.domtree', 690, 360); </script> ````warn header="Tables は常に `<tbody>` を持ちます" -興味深い "特別なケース はテーブルです。DOM仕様によると、それらは `<tbody>` をもたなければなりませんが、HTMLテキストでは(公式に)それを省略することができます。そしてブラウザは DOM の中に自動的に `<tbody>` を生成します。 +興味深い "特別なケース はテーブルです。DOM仕様によると、テーブルは `<tbody>` をもたなければなりませんが、HTMLテキストでは(公式に)それを省略することができます。そしてブラウザは DOM の中に自動的に `<tbody>` を生成します。 次のHTML: @@ -142,12 +160,14 @@ let node5 = {"name":"TABLE","nodeType":1,"children":[{"name":"TBODY","nodeType": drawHtmlTree(node5, 'div.domtree', 600, 200); </script> -分かりますか? `<tbody>` はどこにも出現していません。このような驚きを避けるためにテーブルを使って作業している間は心に留めておくべきです。 +分かりますか? `<tbody>` はどこにも出現していません。このような驚きを避けるため、テーブルを使用する際は心に留めておくべきです。 ```` -## 他のノードタイプ +## 他の種類のノード + +要素とテキストノード以外にも、他の種類のノードがあります。 -より多くのタグを追加し、ページにコメント追加しましょう: +例えば、コメントです。 ```html <!DOCTYPE HTML> @@ -173,13 +193,13 @@ let node6 = {"name":"HTML","nodeType":1,"children":[{"name":"HEAD","nodeType":1, drawHtmlTree(node6, 'div.domtree', 690, 500); </script> -ここで、私達は新しいツリーのノードタイプが見えます -- `#comment` とラベル付された *comment node(コメントノード)* です。 +ここでは新しい種類のツリーノードが見えます -- `#comment` とラベル付された *comment node(コメントノード)* です。 -私達はこう思うかもしれません -- なぜコメントが DOM に追加されるのでしょう? それはどのような方法でも視覚的表現には影響しません。 しかし、ルールがあります。HTMLに何かがある場合は、DOMツリーにもなければなりません。 +こう思うかもしれません -- なぜコメントが DOM に追加されるのでしょう? コメントは視覚的表現には影響しません。が、ルールがあります。HTMLに何かがある場合は、DOMツリーにもなければなりません。 **HTML上のすべて、たとえコメントでも DOM の一部になります。** -HTMLの冒頭にある `<!DOCTYPE...>` ディレクティブでさえ DOM ノードです。`<html>`の直前のDOMツリーにあります。 私たちはそのノードに触れるつもりはないし、その理由で図に描画さえしませんが、そこにはあります。 +HTMLの冒頭にある `<!DOCTYPE...>` ディレクティブでさえ DOM ノードです。`<html>`の直前のDOMツリーにあります。 そのノードに触れるつもりはないので図に描画していませんが、実際にはあります。 ドキュメント全体を表現する `document` オブジェクトは、正式には DOM ノードでもあります。 @@ -188,17 +208,15 @@ HTMLの冒頭にある `<!DOCTYPE...>` ディレクティブでさえ DOM ノー 1. `document` -- DOM に入る "エントリーポイント" 2. 要素ノード -- HTMLタグ, ツリーのビルディングブロック 3. テキストノード -- テキストを含む -4. コメント -- 時々そこに情報をおくことができ、それは表示されません。が JS はDOMからそれを読むことができます。 +4. コメント -- 必要に応じて情報をおくことができ、これは表示されません。が JS はDOMからそれを読むことができます。 -## あなた自身で見てください +## 実際に見てみてください 実際にDOM構造を見るために、[Live DOM Viewer](http://software.hixie.ch/utilities/js/live-dom-viewer/) にトライしてみましょう。ドキュメントに入力するだけで、すぐに DOM が表示されます。 -## ブラウザのインスペクタで +また、DOM を調べる別の方法は、ブラウザの開発者ツールを使うことです。実際、開発するときに使います。 -DOM を調べる別の方法はブラウザの開発者ツールを使うことです。実際、開発するときに使っているものです。 - -それをするために、web ページ [elks.html](elks.html) を開き、ブラウザ開発者ツールを開き、Elements タブに切り替えます。 +これをするには、web ページ [elks.html](elks.html) を開き、ブラウザ開発者ツールを開き、Elements タブに切り替えます。 このようになるはずです: @@ -208,42 +226,46 @@ DOMを見ることができます、要素をクリックしその詳細をみ 開発者ツール上のDOM構造は簡略化されていることに注意してください。テキストノードは単にテキストとして表示されます。また、"空の" (スペースだけの) テキストノードはまったくありません。ほとんどの場合、要素のノードに興味があるのでそれは問題ありません。 -左上隅の <span class="devtools" style="background-position:-328px -124px"></span> ボタンをクリックすると、マウス(または他のポインターデバイス)を使用してWebページからノードを選択できます。 それを "検査"します(Elements(要素) タブでスクロールします)。 巨大なHTMLページがあり、その中の特定の場所のDOMを見たいときにうまくいきます。 +左上隅の <span class="devtools" style="background-position:-328px -124px"></span> ボタンをクリックすると、マウス(または他のポインターデバイス)を使用してWebページからノードを選択できます。 それを "検査"します(Elements(要素) タブでスクロールします)。 巨大なHTMLページがあり、その中の特定の場所のDOMを見たいときに使用します。 -それをする別の方法は、web ページ上で単に右クリックをして、コンテキストメニュー上で "Inspect(検査)" を選択することです。 +これを行う別の方法は、web ページ上で単に右クリックをして、コンテキストメニュー上で "Inspect(検査)" を選択することです。 ![](inspect.png) ツールの右側には、次のようなサブのタブがあります: -- Styles - 組み込みルール(灰色)を含めて、ルール別に現在の要素ルールにCSSが適用されていることがわかります。 ほとんどすべては、下のボックスの大きさ/マージン/パディングを含めて編集できます。 -- Computed - プロパティによって要素に適用されたCSSを表示する:各プロパティに対して、それを与える規則(CSSの継承などを含む)を見ることができます。 -- Event Listeners - DOM要素に関連付けられたイベントリスナーを表示します(チュートリアルの次の部分で説明します)。 +- **Styles** - 組み込みルール(灰色)を含めて、ルール別に現在の要素ルールにCSSが適用されていることがわかります。 ほとんどすべては、下のボックスの大きさ/マージン/パディングを含めて編集できます。 +- **Computed** - プロパティによって要素に適用されたCSSを表示する:各プロパティに対して、それを与える規則(CSSの継承などを含む)を見ることができます。 +- **Event Listeners** - DOM要素に関連付けられたイベントリスナーを表示します(チュートリアルの次の部分で説明します)。 - ...など -それらを学ぶベストな方法は、クリックして回ることです。ほとんどの値はその場で変更可能です。 +これらを学ぶベストな方法は、クリックして回ることです。ほとんどの値はその場で変更可能です。 + +## コンソールでのインタラクション -## コンソールとのインタラクション +DOM を調べるにつれて、DOM に対して JavaScript を適用したいことがあります。例えば、ノードを取得して修正するコードを実行し、その結果を確認する、です。 ここでは、Elementsタブとコンソールの間を移動する tips をいくつか紹介します。 -DOM を調べるにつれて、それに JavaScript を適用したいことがあります。例えば: ノードを取得し、それを修正するコードを実行して、それがどのように見えるかを確認します。 Elementsタブとコンソールの間を移動するヒントをいくつか紹介します。 +まず最初に: -- Elements タブで最初の `<li>` を選択します。 -- `key:Esc` を押す -- Elements タブのすぐ下にコンソールが開きます。 +1. Elements タブで最初の `<li>` を選択します。 +2. `key:Esc` を押します。Elements タブのすぐ下にコンソールが開きます。 これで、最後に選択した要素は `$0` として利用可能で、以前に選択したものは `$1` です。 -我々はそこでコマンドを実行することができます。例えば、`$0.style.background = 'red'` は選択されているリストアイテムを赤にします、このように: +コンソールでコマンドが実行できます。例えば、`$0.style.background = 'red'` は選択されているリストアイテムを赤にします、このように: ![](domconsole0.png) -反対側から、コンソールにいて変数がDOMノードを参照している場合は、`inspect(node)` コマンドを使用してElementsペインに表示することができます。 +これがコンソールで、要素からノードを取得する方法です。 + +逆もあります。DOMノードを参照している変数がある場合、`inspect(node)` コマンドを実行すると、Elements ペインで表示させることができます。 -もしくは単にそれをコンソールに出力し、"その場" で調べることができます。したの `document.body` のように: +もしくは単にそれをコンソールに出力し、"その場" で調べることができます。下の `document.body` のように: ![](domconsole1.png) -これはもちろんデバッグ目的のためです。次のチャプターからはJavaScriptを使ってDOMにアクセスしたり修正したりします。 +これはもちろんデバッグ目的のためです。次の章からはJavaScriptを使ってDOMにアクセスしたり修正したりします。 -ブラウザの開発者ツールは開発を非常に助けます: DOMを調べたり、何かを試みたり何が間違っているかを見たり。 +ブラウザの開発者ツールは、開発で大いに役立ちます: DOMを調べたり、何かを試みたり何が間違っているかを見たり。 ## サマリ @@ -253,8 +275,8 @@ HTML/XML ドキュメントはブラウザ内では DOM ツリーとして表現 - テキストはテキストノードになります。 - ...等、HTML上のすべては DOM にも存在します。たとえコメントでも。 -私たちは、手動でDOMを検査したり修正するために開発者ツールを使うことができます。 +手動でDOMを検査したり修正するのに、開発者ツールが使えます。 -ここでは、基本と、最もよく使われている重要なアクションについて説明しました。 Chrome開発者ツールに関する詳細なドキュメントは、<https://developers.google.com/web/tools/chrome-devtools> にあります。 ツールを学ぶ最も良い方法は、ここをクリックしてメニューを読むことです: ほとんどのオプションは明白です。 後ほど、あなたが一般的にそれらを知っているときは、ドキュメントを読んで残りを拾います。 +ここでは、基本と、最もよく使われている重要なアクションについて説明しました。 Chrome開発者ツールに関する詳細なドキュメントは、<https://developers.google.com/web/tools/chrome-devtools> にあります。 ツールを学ぶ最も良い方法は、ここをクリックしてメニューを読むことです: ほとんどのオプションは明白です。 -DOMノードには、それらの間を移動したり、変更したり、ページの周りを移動したりできるようにするプロパティとメソッドがあります。 次のチャプターでそれらを見ていきましょう。 +DOMノードには、ノード間の移動やノードの変更、ページの遷移と言ったことを可能とするプロパティとメソッドがあります。 次の章でそれらを見ていきましょう。 diff --git a/2-ui/1-document/02-dom-nodes/statusbarButtonGlyphs.svg b/2-ui/1-document/02-dom-nodes/statusbarButtonGlyphs.svg deleted file mode 100644 index fa37b2cc3d..0000000000 --- a/2-ui/1-document/02-dom-nodes/statusbarButtonGlyphs.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="120" width="320"><defs><linearGradient id="p"><stop offset="0" stop-color="#e59290"/><stop offset="1" stop-color="#e99890"/></linearGradient><linearGradient id="o"><stop offset="0" stop-color="#c0544f"/><stop offset="1" stop-color="#d08481"/></linearGradient><linearGradient id="n"><stop offset="0" stop-color="#ffa801" stop-opacity="0"/><stop offset="1" stop-color="#f0fb3d"/></linearGradient><linearGradient id="m"><stop offset="0" stop-color="#ffbd00" stop-opacity=".65"/><stop offset="1" stop-color="#fff" stop-opacity=".906"/></linearGradient><linearGradient><stop offset="0" stop-color="#a16a00"/><stop offset="1" stop-color="#c68200"/></linearGradient><linearGradient id="j"><stop offset="0" stop-color="#00d600" stop-opacity="0"/><stop offset="1" stop-color="#d8fc7b" stop-opacity=".812"/></linearGradient><linearGradient id="i"><stop offset="0" stop-color="#00ba00"/><stop offset="1" stop-color="#fff" stop-opacity=".906"/></linearGradient><linearGradient id="h"><stop offset="0" stop-color="#00a104"/><stop offset="1" stop-color="#00c605"/></linearGradient><linearGradient id="l"><stop offset="0" stop-color="#f00" stop-opacity="0"/><stop offset="1" stop-color="#f0cb68" stop-opacity=".709"/></linearGradient><linearGradient id="k"><stop offset="0" stop-color="#e60000" stop-opacity=".65"/><stop offset="1" stop-color="#fff" stop-opacity=".906"/></linearGradient><linearGradient><stop offset="0" stop-color="#f00"/><stop offset="1" stop-color="#9f0000"/></linearGradient><linearGradient id="g"><stop offset="0" stop-color="#a10000"/><stop offset="1" stop-color="#c60000"/></linearGradient><linearGradient><stop offset="0"/><stop offset="1" stop-opacity="0"/></linearGradient><linearGradient id="f"><stop offset="0"/><stop offset="1" stop-color="#5c5c5c"/></linearGradient><linearGradient id="e"><stop offset="0"/><stop offset="1" stop-color="#929292"/></linearGradient><linearGradient id="d"><stop offset="0" stop-color="#ac1f10"/><stop offset="1" stop-color="#f48f84"/></linearGradient><linearGradient id="c"><stop offset="0" stop-color="#d7687d"/><stop offset="1" stop-color="#b21402"/></linearGradient><linearGradient id="b"><stop offset="0" stop-color="#d76f7d"/><stop offset="1" stop-color="#b21402"/></linearGradient><clipPath><path d="M0 560h960V0H0v560z"/></clipPath><clipPath id="q"><path d="M160 296h16.125v-14H160v14z"/></clipPath><clipPath id="s"><path d="M358 303h22v-4h-22v4z"/></clipPath><clipPath id="r"><path d="M484.867 399.56h22.705v-19.12h-22.705v19.12z"/></clipPath><clipPath><path d="M0 560h960V0H0v560z"/></clipPath><clipPath id="t"><path d="M662 341c0-9.94 8.06-18 18-18 9.942 0 18 8.06 18 18s-8.058 18-18 18c-9.94 0-18-8.06-18-18"/></clipPath><radialGradient cx="0" cy="0" fx="0" fy="0" gradientTransform="matrix(18 0 0 -18 680 341)" gradientUnits="userSpaceOnUse" id="a" r="1" spreadMethod="pad"><stop offset="0"/><stop offset="1" stop-opacity="0"/></radialGradient><clipPath><path d="M0 560h960V0H0v560z"/></clipPath><radialGradient xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="matrix(18 0 0 -18 680 341)" spreadMethod="pad" cx="0" cy="0" fx="0" fy="0" r="1"/><radialGradient xlink:href="#a" gradientUnits="userSpaceOnUse" gradientTransform="matrix(18 0 0 -18 680 341)" spreadMethod="pad" cx="0" cy="0" fx="0" fy="0" r="1"/><radialGradient xlink:href="#a" id="u" gradientUnits="userSpaceOnUse" gradientTransform="matrix(18 0 0 -18 680 341)" spreadMethod="pad" cx="0" cy="0" fx="0" fy="0" r="1"/><linearGradient xlink:href="#b" x2="24" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-1 0 0 -1 24 0)"/><linearGradient xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-1 0 0 -1 24 0)" x2="24"/><linearGradient xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-1 0 0 -1 24 0)" x2="24"/><linearGradient xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-1 0 0 -1 24 0)" x2="24"/><linearGradient xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-1 0 0 -1 24 0)" x2="24"/><linearGradient xlink:href="#c" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-1 0 0 -1 24 0)" x2="24"/><linearGradient xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-1 0 0 -1 24 0)" x2="24"/><linearGradient xlink:href="#c" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-1 0 0 -1 24 0)" x2="24"/><linearGradient xlink:href="#c" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-1 0 0 -1 24 0)" x2="24"/><linearGradient xlink:href="#b" gradientUnits="userSpaceOnUse" gradientTransform="matrix(-1 0 0 -1 24 0)" x2="24"/><linearGradient xlink:href="#d" x2="24" gradientUnits="userSpaceOnUse"/><linearGradient xlink:href="#c" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 -.583 -.583 0 120 111)" x2="24"/><linearGradient xlink:href="#e" x1="113" y1="104" x2="127" y2="104" gradientUnits="userSpaceOnUse"/><linearGradient xlink:href="#f" x1="113" y1="104" x2="127" y2="104" gradientUnits="userSpaceOnUse"/><linearGradient xlink:href="#c" id="v" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 -.583 -.583 0 120 111)" x2="24"/><linearGradient xlink:href="#c" id="y" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 -.583 -.583 0 120 111)" x2="24"/><linearGradient xlink:href="#g" id="z" gradientUnits="userSpaceOnUse" x1="227.875" y1="103.156" x2="235.125" y2="103.156"/><linearGradient xlink:href="#h" id="C" gradientUnits="userSpaceOnUse" x1="227.875" y1="103.156" x2="235.125" y2="103.156"/><linearGradient xlink:href="#i" id="D" gradientUnits="userSpaceOnUse" x1="227.875" y1="103.156" x2="235.125" y2="103.156"/><linearGradient xlink:href="#j" id="E" gradientUnits="userSpaceOnUse" x1="227.875" y1="103.156" x2="235.125" y2="103.156"/><linearGradient xlink:href="#k" id="A" gradientUnits="userSpaceOnUse" x1="227.875" y1="103.156" x2="235.125" y2="103.156"/><linearGradient xlink:href="#l" id="B" gradientUnits="userSpaceOnUse" x1="227.875" y1="103.156" x2="235.125" y2="103.156"/><linearGradient xlink:href="#m" id="F" gradientUnits="userSpaceOnUse" x1="227.875" y1="103.156" x2="235.125" y2="103.156"/><linearGradient xlink:href="#n" id="G" gradientUnits="userSpaceOnUse" x1="227.875" y1="103.156" x2="235.125" y2="103.156"/><linearGradient xlink:href="#o" x1="113" y1="104" x2="127" y2="104" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 -1 1 0 16 224)"/><linearGradient xlink:href="#o" gradientUnits="userSpaceOnUse" x1="113" y1="104" x2="127" y2="104" gradientTransform="matrix(0 -1 1 0 16 224)"/><linearGradient xlink:href="#p" x1="96.5" y1="103" x2="109.5" y2="103" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 1 -1 0 206 0)"/><linearGradient xlink:href="#o" id="w" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 -1 1 0 16 224)" x1="113" y1="104" x2="127" y2="104"/><linearGradient xlink:href="#o" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 -1 1 0 16 224)" x1="113" y1="104" x2="127" y2="104"/><linearGradient xlink:href="#p" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 1 -1 0 206 0)" x1="96.5" y1="103" x2="109.5" y2="103"/><linearGradient xlink:href="#p" id="x" gradientUnits="userSpaceOnUse" gradientTransform="matrix(0 1 -1 0 206 0)" x1="96.5" y1="103" x2="109.5" y2="103"/></defs><g clip-path="url(#q)" opacity=".5" transform="matrix(.5 0 0 -.51 -36.014 209.912)"><path d="M160 296l4-5v-9h8v9l4.125 5H160z" fill="#424242"/></g><g clip-path="url(#r)" opacity=".4" transform="matrix(.507 0 0 -.523 -41.086 214.007)"><path d="M491.55 386.362l-3.62 4.31-3.063-2.57 6.428-7.66 2.834 2.844 13.44 13.443-2.83 2.82-13.2-13.2z"/></g><path d="M57 12l-3.412-4H39v8h14.588M25 36l-3.638-4H19.57L14 40h7.362L25 36zM7 32v8h2.053L14 32H7zm2.668 12.547l-1.594-1.32L18.83 27.454l1.596 1.32L9.668 44.546zM16 15.1c-1.748 0-3.165-1.415-3.165-3.163 0-1.748 1.417-3.165 3.165-3.165V7c-2.762 0-5 2.238-5 5s2.238 5 5 5 5-2.238 5-5h-1.838c-.035 1.718-1.435 3.1-3.162 3.1M16 5l4.5 3-4.5 3m37 26H43v-5h10v5zm-12 5h14V30H41v12zM10 57H8v9h11v-2h-9v-7zm3-1h7v5h-7v-5zm7-2h-9v9h11v-9h-2zm66-21h-8v-2h8v2zm-14.5-1.563l4.2 4.313-4.2 4.313 1.4 1.437 4.2-4.312 1.4-1.438-1.4-1.438L72.9 30l-1.4 1.437zM86 37h-6v-2h6v2zm0 4h-8v-2h8v2zm-5.562-24.062c-2.486 0-4.5-2.015-4.5-4.5 0-.88.256-1.7.693-2.393l6.2 6.2c-.69.436-1.51.692-2.39.692m4.5-4.5c0 .88-.25 1.7-.69 2.393l-6.2-6.2c.7-.436 1.52-.693 2.4-.693 2.483 0 4.5 2.015 4.5 4.5M80.5 6C76.91 6 74 8.91 74 12.5s2.91 6.5 6.5 6.5 6.5-2.91 6.5-6.5S84.09 6 80.5 6M117 65h-10V55h10v10zm-1-28v4h-8v-3h-1v4h10v-5h-1zm-4.116-1.097l-1.832-2.255-1.552 1.345L111.756 39l1.435-1.488L120 30.48 118.568 29l-6.684 6.903zM149 8.333L147.667 7 144 10.667 140.333 7 139 8.333 142.667 12 139 15.667 140.333 17 144 13.333 147.667 17 149 15.667 145.333 12 149 8.333zM46.5 65.235c.32.134.844.242 1.466.242.592 0 1.144-.097 1.534-.263v-3.93l4-4.57v-.19h-11v.217l4 4.57v3.93zM47.966 67c-1.155 0-2.168-.287-2.71-.767L45 66.005v-4.117l-4-4.568V55h14v2.346l-4 4.568v4.133l-.31.228c-.624.46-1.617.725-2.724.725M86 60l-10-5v10"/><g clip-path="url(#s)" opacity=".2" transform="matrix(.5 0 0 -.5 -37 206.5)"><path d="M380 299h-22v4h22v-4z"/></g><path d="M135 54v-1h-2v14h2v-1h-1V54h1zm7 3h-6v-2h6v2zm3 3h-6v-2h6v2zm5 3h-6v-2h6v2zm2 3h-6v-2h6v2zm18 1h-2v-9h2v9zm3 0h-2V56h2v11zm3 0h-2V54h2v13zm3 0h-2v-4h2v4zm3 0h-2v-9h2v9zm3 0h-2V56h2v11zm23-10h-10v-2h10v2zm9 0h-5v-2h5v2zm-17 4h-2v-2h2v2zm13 0h-10v-2h10v2zm4 0h-2v-2h2v2zm-12 4h-7v-2h7v2zm12 0h-8v-2h8v2zm57-1h-7v-8h7v8zm-9 2h14V54h-14v12zm33-54c0 3.313 2.686 6 6 6s6-2.687 6-6-2.686-6-6-6-6 2.687-6 6"/><g clip-path="url(#t)" transform="matrix(.5 0 0 -.5 -36 206.5)"><path d="M662 341c0-9.94 8.06-18 18-18 9.942 0 18 8.06 18 18s-8.058 18-18 18c-9.94 0-18-8.06-18-18" fill="url(#u)"/></g><path d="M298 36c0 3.313 2.686 6 6 6s6-2.687 6-6-2.686-6-6-6-6 2.687-6 6"/><path d="M239.063 38.104c-1.68 0-3.042-1.36-3.042-3.04s1.37-3.043 3.05-3.043 3.04 1.37 3.04 3.05-1.36 3.04-3.04 3.04m7.938 3.46l-4.077-4.073c.443-.705.703-1.535.703-2.427 0-2.52-2.042-4.564-4.562-4.564-2.52 0-4.562 2.042-4.562 4.562s2.044 4.562 4.564 4.562c.89 0 1.72-.26 2.426-.704l4.076 4.08L247 41.57z"/><path d="M0 0l10.75 6.5L21.625 2l10.125 8.125" fill="none" stroke="#000" stroke-width="3" stroke-miterlimit="10" transform="matrix(.489 0 0 -.497 233.733 63.763)"/><path d="M231 67V54h-1v14h21.012v-1h-20.01zm6-56h-4V7h4v4zm10-2h-9V7h9v2zm0 2h-9v-1h9v1zm0 4h-9v-2h9v2zm-10 2h-4v-4h4v4zm10 0h-9v-1h9v1zm-71 21.133c-1.178 0-2.133-.955-2.133-2.133s.955-2.133 2.133-2.133 2.133.955 2.133 2.133-.955 2.133-2.133 2.133m8-3.733l-2.933-.533 1.72-2.392-2.262-2.263L178.067 31l-.467-3h-3.2l-.4 3.133-2.525-1.92-2.263 2.262 1.988 2.592-3.2.333v3.2l3.333.333-2.12 2.592 2.262 2.263 2.525-2.12.4 3.332h3.2l.4-3.2 2.525 1.988 2.263-2.263L181 38.067l3-.467v-3.2zm-9.03-15.775c0-.9.426-1.348 1.28-1.348.417 0 .737.117.957.353.22.234.33.566.33.995 0 .423-.11.758-.334 1.005-.223.246-.542.37-.954.37-.42 0-.73-.12-.95-.362-.22-.24-.33-.58-.33-1.013m.35-2.848v-.57c0-.656.11-1.2.35-1.635.23-.435.64-.884 1.22-1.35.69-.558 1.13-.993 1.33-1.302.2-.31.3-.68.3-1.108 0-.5-.162-.883-.487-1.15-.326-.27-.793-.403-1.403-.403-.55 0-1.06.08-1.53.24-.47.16-.93.35-1.377.58L173 7.51c1.18-.674 2.442-1.01 3.79-1.01 1.137 0 2.04.286 2.708.857.668.572 1.002 1.36 1.002 2.366 0 .447-.064.844-.19 1.192-.13.348-.323.68-.58.996-.26.32-.705.73-1.338 1.24-.54.44-.902.8-1.085 1.08-.183.29-.274.67-.274 1.15v.4h-1.707zm35.01 22.55c0 .793.08 1.387.24 1.78.16.394.42.59.78.59.71 0 1.062-.79 1.062-2.37 0-1.56-.356-2.34-1.067-2.34-.358 0-.617.193-.777.575-.16.384-.24.972-.24 1.765m3.663 0c0 1.224-.226 2.142-.676 2.755-.45.612-1.108.918-1.97.918-.826 0-1.466-.315-1.92-.946-.458-.63-.686-1.54-.686-2.727 0-2.432.867-3.65 2.604-3.65.836 0 1.486.315 1.95.944.464.63.696 1.53.696 2.71m-2.42-8.16l-6.6 11.68h-1.58l6.6-11.67h1.58zm-8.995 3.5c0 .8.08 1.39.233 1.77.16.39.416.59.775.59.716 0 1.074-.784 1.074-2.354s-.356-2.356-1.072-2.356c-.36 0-.616.192-.774.58-.157.388-.236.98-.236 1.773m3.664-.016c0 1.224-.224 2.14-.67 2.758-.45.616-1.11.923-1.983.923-.826 0-1.466-.32-1.92-.956-.454-.635-.68-1.544-.68-2.725 0-2.433.866-3.65 2.597-3.65.85 0 1.505.316 1.964.947.46.63.69 1.533.69 2.704M268 35.994c.42.23.71.494.87.8.16.306.24.83.24 1.57v1.02c0 .47.046.76.138.883.092.12.265.203.522.245.06.013.15.027.273.04.63.077.945.33.945.757 0 .21-.092.37-.275.5-.184.13-.428.19-.733.19-.39 0-.765-.05-1.128-.16-.364-.1-.665-.25-.904-.43-.26-.2-.445-.457-.553-.77-.11-.31-.163-.85-.163-1.61v-.82c0-.834-.46-1.313-1.382-1.44-.04-.01-.066-.01-.083-.01-.27-.037-.466-.117-.59-.238-.13-.123-.195-.298-.195-.527 0-.2.053-.36.162-.477.107-.118.28-.2.52-.253.105-.026.257-.056.457-.09.74-.113 1.11-.57 1.11-1.37v-.822c0-.706.04-1.202.13-1.486.08-.285.222-.533.422-.745.234-.237.546-.42.937-.553.39-.13.82-.2 1.292-.2.29 0 .53.064.706.19.178.128.267.296.267.504 0 .435-.337.697-1.01.785-.064.002-.107.007-.136.012-.28.04-.47.12-.578.254-.107.13-.16.43-.16.895v1.02c0 .693-.083 1.203-.25 1.527-.164.324-.45.593-.86.806m8.977-.008c-.404-.215-.687-.48-.854-.807-.168-.324-.25-.832-.25-1.526v-1.45c0-.214-.068-.377-.2-.49-.135-.112-.36-.187-.67-.223-.407-.048-.677-.127-.81-.24-.135-.11-.2-.28-.2-.51 0-.235.083-.417.252-.55.17-.13.41-.194.72-.194.41 0 .792.05 1.146.153.358.1.655.243.89.43.276.206.466.464.577.77.11.307.165.85.165 1.626v.82c0 .8.38 1.26 1.14 1.385.186.02.326.045.426.068.235.053.406.14.518.26.11.12.168.28.168.477 0 .206-.06.372-.177.5-.116.122-.29.21-.524.256l-.376.056c-.78.126-1.176.596-1.176 1.41v.806c0 .73-.04 1.24-.124 1.522-.08.286-.222.528-.422.727-.218.217-.53.394-.937.53-.404.137-.83.205-1.274.205-.31 0-.555-.066-.73-.2-.175-.13-.26-.315-.26-.552 0-.217.064-.38.195-.493.13-.11.403-.195.816-.25.34-.04.57-.12.69-.23.12-.11.18-.29.18-.534v-1.39c0-.74.08-1.262.24-1.564.16-.303.447-.568.86-.797m-1.743-21.297h-2.153v-5.38h2.152v5.384zm-4.307 0h-2.154v-5.38h2.15v5.384zm4-9.692h-5.846L265 9.04v5.844L269.077 19h5.846L279 14.882V9.038L274.923 5zM112.03 15.5c-1.932 0-3.5-1.567-3.5-3.5s1.568-3.5 3.5-3.5c1.934 0 3.5 1.567 3.5 3.5s-1.566 3.5-3.5 3.5M112 7c-2.437 0-4.625.834-9 5 4.312 4.167 6.563 5 9 5 2.437 0 4.625-.834 9-5-4.312-4.167-6.563-5-9-5m-1.5 5c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5-.67-1.5-1.5-1.5-1.5.67-1.5 1.5m29 21h9L147 43h-6m6.5-13h-2v-1h-3v1h-2c-.552 0-1 .482-1 1v1h9v-1c0-.518-.448-1-1-1M317 69v-5l-5 5h5zM212 13v4h-8v-3h-1v4h10v-5h-1zM23 84l-8-5.002v10.004M13 89h-3V79h3v10zm34-1h-3v-8h3v8zm6 0h-3v-8h3v8zm25.5 1c0 1.107.897 2.004 2.004 2.004s2.004-.897 2.004-2.004-.897-2.004-2.004-2.004S78.5 87.893 78.5 89m2-3l4.498-4.01h-8.996M82 82h-3v-4h3v4zm28.5 7c0 1.107.897 2.004 2.004 2.004s2.004-.897 2.004-2.004-.897-2.004-2.004-2.004-2.004.897-2.004 2.004m2-11l4.498 4.01h-8.996M112.5 78l4.498 4.01h-8.996M114 82h-3v4h3v-4zm28.184 4.433c0 1.107.897 2.004 2.004 2.004s2.004-.897 2.004-2.004-.897-2.004-2.004-2.004-2.004.89-2.004 2"/><path d="M137.25 87.032c2.553-8.432 11.403-8.73 13.938 0" fill="none" stroke="#000" stroke-width="2.5"/><path d="M151.676 89l-4.538-2.77 6.682-2.097M151.676 89l-4.538-2.77 6.682-2.097M12 102l-7-4v8m19 0l4-7h-8M8 111l4 7H4"/><path d="M54 105v7H44v-7zm1 8v-11H43v11z"/><rect width="12" height="3" x="73" y="110" ry="0" opacity=".819"/><path fill="none" stroke="#000" stroke-width="1.937" d="M336.903 158.803h28.195v22.195h-28.195z" transform="matrix(.497 0 0 .537 1.211 -6.719)"/><path d="M179.8 84.516l-4.992-3.25v6.5"/><path d="M171.918 78.032h1v13.004h-1z"/><path fill="none" stroke="#000" stroke-width="1.937" d="M336.903 158.803h28.195v22.195h-28.195z" transform="matrix(.497 0 0 .537 33.211 -6.719)"/><path d="M206.808 84.515l4.993-3.25v6.5"/><path d="M203.918 77.953h1v12.992h-1z"/><path fill="none" stroke="#000" stroke-width="1.937" d="M336.903 158.803h28.195v22.195h-28.195z" transform="matrix(.497 0 0 .537 65.211 -6.719)"/><path d="M235.918 77.953h1v12.992h-1z"/><path fill="none" stroke="#000" stroke-width="1.937" d="M336.903 158.803h28.195v22.195h-28.195z" transform="matrix(-.497 0 0 .537 446.789 -6.719)"/><path d="M273.192 84.516l-4.993-3.25v6.5"/><path d="M276.082 78.032h-1v13.004h1z"/><path fill="none" stroke="#000" stroke-width="1.937" d="M336.903 158.803h28.195v22.195h-28.195z" transform="matrix(-.497 0 0 .537 477.789 -6.719)"/><path d="M299.2 84.516l4.992-3.25v6.5"/><path d="M307.082 78.032h-1v13.004h1z"/><path d="M120 97c-3.866 0-7 3.134-7 7s3.134 7 7 7 7-3.134 7-7-3.134-7-7-7" fill="url(#v)" transform="translate(-2 -1)"/><path d="M111.5 103c0 3.59 2.91 6.5 6.5 6.5s6.5-2.91 6.5-6.5-2.91-6.5-6.5-6.5-6.5 2.91-6.5 6.5" fill="#f27d82"/><path d="M121.5 100.933l-.933-.933-2.567 2.567-2.567-2.567-.933.933 2.566 2.567-2.566 2.567.933.933 2.567-2.567 2.567 2.567.933-.933-2.567-2.567 2.567-2.567z" fill-opacity=".364"/><path d="M121.5 100.433l-.933-.933-2.567 2.567-2.567-2.567-.933.933 2.566 2.567-2.566 2.567.933.933 2.567-2.567 2.567 2.567.933-.933-2.567-2.567 2.567-2.567z" fill="#fff"/><path d="M138.5 100.933l-.933-.933-2.567 2.567-2.567-2.567-.933.933 2.566 2.567-2.566 2.567.933.933 2.567-2.567 2.567 2.567.933-.933-2.567-2.567 2.567-2.567z" fill-opacity=".244"/><path d="M138.5 100.433l-.933-.933-2.567 2.567-2.567-2.567-.933.933 2.566 2.567-2.566 2.567.933.933 2.567-2.567 2.567 2.567.933-.933-2.567-2.567 2.567-2.567z" fill="#676767"/><path d="M120 97c-3.866 0-7 3.134-7 7s3.134 7 7 7 7-3.134 7-7-3.134-7-7-7" fill="url(#w)" transform="matrix(-1 0 0 -1 223 207)"/><path d="M103 96.5c-3.59 0-6.5 2.91-6.5 6.5s2.91 6.5 6.5 6.5 6.5-2.91 6.5-6.5-2.91-6.5-6.5-6.5" fill="url(#x)"/><path d="M106.5 100.933l-.933-.933-2.567 2.567-2.567-2.567-.933.933 2.567 2.567-2.567 2.567.933.933 2.567-2.567 2.567 2.567.933-.933-2.567-2.567 2.567-2.567z" fill="#993c35"/><path d="M106.5 100.433l-.933-.933-2.567 2.567-2.567-2.567-.933.933 2.567 2.567-2.567 2.567.933.933 2.567-2.567 2.567 2.567.933-.933-2.567-2.567 2.567-2.567z" fill="#fff"/><path d="M143 102.5c0 3.59 2.91 6.5 6.5 6.5s6.5-2.91 6.5-6.5-2.91-6.5-6.5-6.5-6.5 2.91-6.5 6.5" fill="#bebebe"/><path d="M153 100.433l-.933-.933-2.567 2.567-2.567-2.567-.933.933 2.566 2.567-2.566 2.567.933.933 2.567-2.567 2.567 2.567.933-.933-2.567-2.567 2.567-2.567z" fill-opacity=".373"/><path d="M153 99.933l-.933-.933-2.567 2.567L146.933 99l-.933.933 2.566 2.567-2.566 2.567.933.933 2.567-2.567 2.567 2.567.933-.933-2.567-2.567L153 99.933z" fill="#fff"/><path d="M160 102.5c0 3.59 2.91 6.5 6.5 6.5s6.5-2.91 6.5-6.5-2.91-6.5-6.5-6.5-6.5 2.91-6.5 6.5" fill="#9f9f9f"/><path d="M170 100.433l-.933-.933-2.567 2.567-2.567-2.567-.933.933 2.566 2.567-2.566 2.567.933.933 2.567-2.567 2.567 2.567.933-.933-2.567-2.567 2.567-2.567z" fill-opacity=".364"/><path d="M170 99.933l-.933-.933-2.567 2.567L163.933 99l-.933.933 2.566 2.567-2.566 2.567.933.933 2.567-2.567 2.567 2.567.933-.933-2.567-2.567L170 99.933z" fill="#fff"/><path d="M184.5 100.433l-.933-.933-2.567 2.567-2.567-2.567-.933.933 2.566 2.567-2.566 2.567.933.933 2.567-2.567 2.567 2.567.933-.933-2.567-2.567 2.567-2.567z" fill-opacity=".244"/><path d="M184.5 99.933l-.933-.933-2.567 2.567L178.433 99l-.933.933 2.566 2.567-2.566 2.567.933.933 2.567-2.567 2.567 2.567.933-.933-2.567-2.567 2.567-2.567z" fill="#676767"/><path d="M131.65 116.212l-1.436-2.03-1.214 1.212 2.548 3.606 1.124-1.34 5.328-6.328-1.12-1.332-5.23 6.212z"/><path d="M195.75 97.75l3.5 3.25-3.5 3.25" fill="none" stroke="#367cf1" stroke-width="1.5"/><path d="M195.75 108.75l3.5 3.25-3.5 3.25" fill="none" stroke="#939393" stroke-width="1.5"/><path d="M209 101c0 .55.446.996.996.996s.996-.446.996-.996-.446-.996-.996-.996-.996.446-.996.996" fill="#bababa"/><path d="M208.25 97.75l-3.5 3.25 3.5 3.25" fill="none" stroke="#bababa" stroke-width="1.5"/><path d="M120 97c-3.866 0-7 3.134-7 7s3.134 7 7 7 7-3.134 7-7-3.134-7-7-7" fill="url(#y)" transform="matrix(.714 0 0 .714 132.286 26.714)"/><path d="M213.357 101c0 2.564 2.08 4.643 4.643 4.643 2.564 0 4.643-2.08 4.643-4.643 0-2.564-2.08-4.643-4.643-4.643-2.564 0-4.643 2.08-4.643 4.643" fill="#eb3941"/><path d="M216 99l4 4m0-4l-4 4m0-4l4 4m0-4l-4 4" fill="none" stroke="#fff"/><path d="M203 116l4-7.778 4 7.778z" fill="none" stroke="#c19600" stroke-width="2" stroke-linecap="square" stroke-linejoin="round"/><path d="M203.017 116l4-7.778 4 7.778z" fill="#f4bd00" stroke="#f5be00" stroke-width="1.5" stroke-linecap="square" stroke-linejoin="round"/><text y="146.82" x="161.332" style="line-height:125%;-inkscape-font-specification:Times Bold" transform="scale(1.263 .792)" font-size="11.261" font-weight="bold" letter-spacing="0" word-spacing="0" fill="#ad8601" font-family="Sans"><tspan y="146.82" x="161.332">!</tspan></text><path style="line-height:125%;-inkscape-font-specification:Times Bold" d="M206 110h2v2.303l-.283 1.68h-1.434l-.283-1.68V110m0 4.57h2V116h-2v-1" font-size="9.677" font-weight="bold" letter-spacing="0" word-spacing="0" fill="#fff" font-family="Sans"/><path d="M235.125 103.156a3.625 3.844 0 1 1-7.25 0 3.625 3.844 0 1 1 7.25 0z" transform="matrix(0 1.379 -1.301 0 363.085 -218.353)" fill="url(#z)"/><path d="M233.5 101c0 3.463-3.748 5.63-6.75 3.898-1.39-.804-2.25-2.29-2.25-3.897 0-3.46 3.748-5.63 6.75-3.89 1.39.807 2.25 2.29 2.25 3.9z" fill="#d00"/><path d="M235.125 103.156a3.625 3.844 0 1 1-7.25 0 3.625 3.844 0 1 1 7.25 0z" transform="matrix(0 -.629 .927 0 133.469 244.528)" fill="url(#A)"/><path transform="matrix(0 .44 .78 0 148.487 2.121)" d="M235.125 103.156a3.625 3.844 0 1 1-7.25 0 3.625 3.844 0 1 1 7.25 0z" fill="url(#B)"/><path d="M235.125 103.156a3.625 3.844 0 1 1-7.25 0 3.625 3.844 0 1 1 7.25 0z" transform="matrix(-.015 1.379 -1.301 -.014 377.665 -216.821)" fill="url(#C)"/><path d="M244.5 101.05c-.038 3.463-3.812 5.587-6.792 3.822-1.384-.82-2.225-2.314-2.208-3.92.038-3.465 3.812-5.59 6.792-3.824 1.384.82 2.225 2.314 2.208 3.92z" fill="#00be00"/><path d="M235.125 103.156a3.625 3.844 0 1 1-7.25 0 3.625 3.844 0 1 1 7.25 0z" transform="matrix(.007 -.629 .927 .01 142.865 243.443)" fill="url(#D)"/><path transform="matrix(-.005 .44 .78 .009 160.603 1.16)" d="M235.125 103.156a3.625 3.844 0 1 1-7.25 0 3.625 3.844 0 1 1 7.25 0z" fill="url(#E)"/><path d="M251.002 106c-3.85 0-6.256-4.165-4.333-7.5.89-1.546 2.54-2.5 4.32-2.5 3.85 0 6.253 4.165 4.33 7.5-.893 1.546-2.543 2.5-4.33 2.5z" fill="#e5a600"/><path d="M255.5 101c0 3.463-3.748 5.63-6.75 3.898-1.39-.804-2.25-2.29-2.25-3.897 0-3.46 3.748-5.63 6.75-3.89 1.39.807 2.25 2.29 2.25 3.9z" fill="#ffbd00"/><path d="M235.125 103.156a3.625 3.844 0 1 1-7.25 0 3.625 3.844 0 1 1 7.25 0z" transform="matrix(0 -.629 .927 0 155.469 244.528)" fill="url(#F)"/><path transform="matrix(0 .44 .78 0 170.443 2.076)" d="M235.125 103.156a3.625 3.844 0 1 1-7.25 0 3.625 3.844 0 1 1 7.25 0z" fill="url(#G)"/></svg> \ No newline at end of file diff --git a/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/task.md b/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/task.md index 7adfca1260..3c951bcccc 100644 --- a/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/task.md +++ b/2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/task.md @@ -6,7 +6,7 @@ importance: 5 対角線上のすべての表のセルを赤でペイントするコードを書いてください。 -`<table>` からすべての体格 `<td>` を取得し、コードを使ってペイントする必要があります。: +`<table>` からすべての対角 `<td>` を取得し、コードを使ってペイントする必要があります。: ```js // td はテーブルセルへの参照 diff --git a/2-ui/1-document/03-dom-navigation/article.md b/2-ui/1-document/03-dom-navigation/article.md index 226a10861b..0757e1a8e2 100644 --- a/2-ui/1-document/03-dom-navigation/article.md +++ b/2-ui/1-document/03-dom-navigation/article.md @@ -5,39 +5,37 @@ libs: --- -# DOM を歩く +# DOM ナビゲーション -DOM は要素やそれらのコンテンツに対して何でもすることができますが、最初に対応する DOM オブジェクトに到達して、変数に入れる必要があります。それから要素やコンテンツを変更することができます。 +DOM は要素やコンテンツに対して様々なことができますが、最初に対応する DOM オブジェクトに到達する必要があります。 DOM 上のすべての操作は `document` オブジェクトから始まります。そこから任意のノードにアクセスできます。 -[cut] - これは DOM ノード間を移動できるリンクの図です。: -![](dom-links.png) +![](dom-links.svg) -それらについてより深く議論しましょう。 +これらについてより深く議論しましょう。 ## トップ: documentElement と body -一番上のツリーノードは `documet` のプロパティとして直接利用可能です: +最上位のツリーノードは `document` プロパティとして直接利用可能です: `<html>` = `document.documentElement` -: 一番上のドキュメントノードは `document.documentElement` です。 それは `<html>` タグの DOM ノードです。 +: 最上位のドキュメントノードは `document.documentElement` で、`<html>` タグの DOM ノードです。 `<body>` = `document.body` -: 別の広く使われている DOM ノードは `<body>` 要素です -- `document.body`. +: もう1つの広く使われている DOM ノードは `<body>` 要素です -- `document.body`. `<head>` = `document.head` : `<head>` タグは `document.head` で利用可能です。 ````warn header="落とし穴があります: `document.body` は `null` になる場合があります" -スクリプトは実行中に存在しない要素へアクセスすることができません。 +スクリプトは実行時に存在しない要素へアクセスできません。 -特に、もしスクリプトが `<head>` の内側にある場合、`document.body` は利用できません。なぜならブラウザはまだ body を呼んでいないからです。 +特に、スクリプトが `<head>` の内側にいる場合、ブラウザはまだ body を読み込んでいないため、`document.body` は利用できません。 -従って、下の例では最初の `alert` は `null` を表示します: +従って、以下の例では最初の `alert` は `null` を表示します: ```html run <html> @@ -67,10 +65,10 @@ DOM では、`null` 値は "存在しない" もしくは "このようなノー ## 子: childNodes, firstChild, lastChild -我々がこれから使う2つの用語があります。: +これから使う2つの用語があります。: -- **子ノード (または子)** -- 直接の子要素です。言い換えると、それらは与えられた要素の中にネストされています。例えば `<head>` と `<body>` は `<html>` 要素の子です。 -- **子孫** -- 子要素、子要素など、指定された要素にネストされたすべての要素です。 +- **子ノード (または子)** -- 直接の子要素です。つまり、与えられた要素にネストされています。例えば `<head>` と `<body>` は `<html>` 要素の子です。 +- **子孫** -- 子要素、さらにその子要素など、指定された要素にネストされたすべての要素です。 例えば、ここで `<body>` は子 `<div>` と `<ul>` (といくつかの空のテキストノード)を持ちます。: @@ -88,11 +86,11 @@ DOM では、`null` 値は "存在しない" もしくは "このようなノー </html> ``` -...また、 `<body>` のすべての子孫について尋ねられた場合、直接の子 `<div>`, `<ul>` と `<li>` (`<ul>` の子) や `<b>` (`<li>` の子)のような、よりネストされた要素を取得 -- サブツリー全体です。 +...また、 `<body>` のすべての子孫は、直接の子 `<div>`, `<ul>` だけでなく、`<li>` (`<ul>` の子) や `<b>` (`<li>` の子)のような、さらにネストされた要素を含む -- サブツリー全体です。 -**`子ノード` のコレクションは、テキストノードを含むすべての子ノードへのアクセスを提供します。** +**`childNodes` のコレクションは、テキストノードを含むすべての子ノードを持ちます。** -下の例は、`document.body` の子を表示します: +以下は `document.body` の子を表示します: ```html run <html> @@ -117,11 +115,11 @@ DOM では、`null` 値は "存在しない" もしくは "このようなノー </html> ``` -ここでの興味深い詳細について注意してください。上の例を実行するとき、表示される最後の要素は `<script>` です。実際には、ドキュメントは下により多くのものを持っていますが、スクリプト実行時点でブラウザはまだそれを読んでいないため、スクリプトはそれを見ません。 +ここで興味深い点に注目してください。上の例を実行すると、表示される最後の要素は `<script>` です。実際には、ドキュメントはそれ以降により多くのものを持ちますが、スクリプト実行時点でブラウザはまだそれらは読み込んでいないため、スクリプトにそれらは見えません。 **プロパティ `firstChild` と `lastChild` で最初と最後の子への高速なアクセスができます** -それらは単なる簡略表記です。子ノードが存在する場合は、常に次のようになります: +これらは単なる簡略表記です。子ノードが存在する場合は、常に次のようになります: ```js elem.childNodes[0] === elem.firstChild elem.childNodes[elem.childNodes.length - 1] === elem.lastChild @@ -131,11 +129,11 @@ elem.childNodes[elem.childNodes.length - 1] === elem.lastChild ### DOM コレクション -ご覧の通り、`childNodes` は配列のように見えます。しかし実際には配列ではなくむしろ *コレクション* -- 特別な配列ライクで反復可能なオブジェクトです。 +ご覧の通り、`childNodes` は配列のように見えます。が、実際には配列ではなくむしろ *コレクション* -- 特別な配列ライクで反復可能なオブジェクトです。 -2つの重要な結果があります: +2つの重要な点があります: -1. それを反復するために `for..of` を使うことができます: +1. 反復する際 `for..of` が使えます: ```js for (let node of document.body.childNodes) { alert(node); // コレクションのすべてのノードを表示する @@ -143,36 +141,35 @@ elem.childNodes[elem.childNodes.length - 1] === elem.lastChild ``` これは反復可能(必須で `Symbol.iterator` プロパティを提供する)のためです。 -2. 配列メソッドは動作しません、なぜなら配列ではないからです: +2. 配列ではないため、配列メソッドは動作しません: ```js run alert(document.body.childNodes.filter); // undefined (フィルタメソッドを持っていません!) ``` -最初の1つ目は良いです。2つ目は許容できます。なぜなら、配列メソッドが必要な場合、コレクションから "本当の" 配列を作るために `Array.from` を使うことができるからです。: +最初の1つ目は良いです。2つ目も、配列メソッドが必要な場合は、`Array.from` でコレクションから "本当の" 配列を作ることができるので許容できます。: ```js run alert( Array.from(document.body.childNodes).filter ); // これで使えます ``` ```warn header="DOM コレクションは読み取り専用です" -DOM コレクションやさらに -- このチャプターにリストされている *すべての* ナビゲーションプロパティは読み取り専用です。 +DOM コレクションやさらに -- この章でリストされている *すべての* ナビゲーションプロパティは読み取り専用です。 -代入 `childNodes[i] = ...` などにより子ノードを置き換えることはできません。 +代入 `childNodes[i] = ...` などで子ノードを置き換えることはできません。 -DOM の変更は他のメソッドを必要とします。それらについては次のチャプターで見ていきましょう。 +DOM の変更は他のメソッドを必要とします。それらについては次の章で見ていきましょう。 ``` ```warn header="DOM コレクションはライブです" -マイナーな例外を伴うほとんどすべての DOM コレクションは *ライブ* です 。つまり、それらは DOM の現在の状態を反映しています。 +一部の例外を除き、ほぼすべての DOM コレクションは *ライブ* です 。つまり、それらは DOM の現在の状態を反映しています。 もし `elem.childNodes` への参照を維持し、DOM にノードを追加/削除すると、コレクションの中に自動的に反映されます。 ``` -````warn header="コレクションをループするために、`for..in` を使わないでください" -Collections are iterable using `for..of`. Sometimes people try to use `for..in` for that. +````warn header="コレクションのループで、`for..in` を使わないでください" +コレクションは `for..of` で反復可能です。時々 `for..in` を使おうとする人がいます。 -使わないでください。`for..in` ループはすべての列挙可能なプロパティを反復します。そしてコレクションは, -通常取得したいと思わないいくつかの "余分な" ほとんど使われないプロパティを持っています。: +`for..in` は使わないでください。`for..in` ループはすべての列挙可能なプロパティを反復します。コレクションは、通常は取得不要ないくつかの "余分な" ほとんど使われないプロパティを持っています: ```html run <body> @@ -185,21 +182,26 @@ Collections are iterable using `for..of`. Sometimes people try to use `for..in` ## 兄弟と親 -*兄弟(Siblings)* は同じ親(parent)の子ノードです。例えば、`<head>` と `<body>` は兄弟です: +*兄弟(Siblings)* は同じ親(parent)の子ノードです。 + +例えば、`<head>` と `<body>` は兄弟です: + +```html +<html> + <head>...</head><body>...</body> +</html> +``` - `<body>` は `<head>` の "次の" または "右の" 兄弟と言われます。 - `<head>` `<body>` の "前の" または "左の" 兄弟と言われます。 -親は `parentNode` として利用可能です。 +次のノード(次の兄弟) は `nextSibling` であり、前のノードは `previousSibling` です。 -同じ親において、次のノード(次の兄弟) は `nextSibling` であり、前のノードは `previousSibling` です。 +親は `parentNode` として利用可能です。 例えば: -```html run -<html><head></head><body><script> - // HTML は余分な "ブランクの" テキストノードを避けるために密集しています。 - +```js run // <body> の親は <html> です。 alert( document.body.parentNode === document.documentElement ); // true @@ -208,18 +210,17 @@ Collections are iterable using `for..of`. Sometimes people try to use `for..in` // <body> の前は <head> です。 alert( document.body.previousSibling ); // HTMLHeadElement -</script></body></html> ``` ## Element-only navigation -上でリストされているナビゲーションプロパティは *すべての* ノードを参照しています。例えば、`childNodes` では、テキストノード、要素ノードの両方を、存在する場合にはコメントノードも見ることができます。 +上でリストされているナビゲーションプロパティは *すべての* ノードを参照します。例えば、`childNodes` では、テキストノード、要素ノードの両方を、さらに存在する場合にはコメントノードも見ることができます。 -しかし、多くのタスクでは、テキストノードやコメントノードは必要ありません。 タグを表し、ページの構造を形成する要素ノードを操作したいです。 +しかし、多くのタスクでは、テキストノードやコメントノードは必要ありません。タグを表し、ページの構造を形成する要素ノードを操作したいです。 なので、*要素ノード* だけを考慮にいれたナビゲーションリンクをもっと見てみましょう: -![](dom-links-elements.png) +![](dom-links-elements.svg) リンクは上で与えられたものと似ており、`Element` という言葉が内部にあります: @@ -238,8 +239,14 @@ alert( document.documentElement.parentNode ); // document alert( document.documentElement.parentElement ); // null ``` -言い換えると、`documentElement` (`<html>`) はルートノードです。公式にはその親として `document` を持っています。しかし、`document` は要素ノードではないので、`parentNode` はそれを返し、`parentElement` はそうではありません。 +言い換えると、`documentElement` (`<html>`) はルートノードです。公式にはその親として `document` を持っています。しかし、`document` は要素ノードではないので、`parentNode` はそれを返し、`parentElement` は返しません。 +これは、任意の要素 `elem` から `<html>` に移動したいが、`document` には移動したくな場合に役立ちます: +```js +while(elem = elem.parentElement) { // go up till <html> + alert( elem ); +} +``` ```` 上の例の1つを修正してみましょう: `childNodes` を `children` に置き換えます。これで要素のみが表示されます。: @@ -304,8 +311,9 @@ alert( document.documentElement.parentElement ); // null </table> <script> - // 最初の行の2つ目のセルのコンテンツを取得 - alert( table.*!*rows[0].cells[1]*/!*.innerHTML ) // "two" + // "two" の td を取得 + let td = table.*!*rows[0].cells[1]*/!*; + td.style.backgroundColor = "red"; // ハイライト </script> ``` diff --git a/2-ui/1-document/03-dom-navigation/dom-links-elements.png b/2-ui/1-document/03-dom-navigation/dom-links-elements.png deleted file mode 100644 index f7eef5d1f7..0000000000 Binary files a/2-ui/1-document/03-dom-navigation/dom-links-elements.png and /dev/null differ diff --git a/2-ui/1-document/03-dom-navigation/dom-links-elements.svg b/2-ui/1-document/03-dom-navigation/dom-links-elements.svg new file mode 100644 index 0000000000..fd0b2826a4 --- /dev/null +++ b/2-ui/1-document/03-dom-navigation/dom-links-elements.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="440" height="316" viewBox="0 0 440 316"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="dom-links-elements.svg"><path id="Rectangle-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M129 10h198v28H129z"/><text id="document.documentEle" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="142.6" y="29">document.documentElement </tspan></text><text id="Type-something" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="336.9" y="29"><HTML></tspan></text><path id="Rectangle-7" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M163 78h117v28H163z"/><text id="document.body--" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="174.2" y="95">document.body </tspan></text><text id="(if-inside-body)" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="300.9" y="95">(if inside body)</tspan></text><path id="Line-5" stroke="#AF6E24" stroke-linecap="square" stroke-width="2" d="M14.5 115H427"/><text id="parentElement" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="175" y="172" fill="#AF6E24">parent</tspan> <tspan x="218.2" y="172" fill="#C06334">Element</tspan></text><path id="Rectangle-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M180 213h80v28h-80z"/><text id="<DIV>" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="12" font-weight="normal"><tspan x="204.192" y="232"><DIV></tspan></text><path id="Line-6" fill="#C06334" fill-rule="nonzero" d="M220.5 178.71l.872 1.55 6.75 12 .49.871-1.743.98-.49-.87-4.879-8.674V208.5h-2v-23.933l-4.878 8.673-.49.872-1.744-.98.49-.872 6.75-12 .872-1.55z"/><path id="Line-7" fill="#C06334" fill-rule="nonzero" d="M415.369 218.388l.871.49 12 6.75 1.55.872-1.55.872-12 6.75-.871.49-.98-1.743.87-.49 8.673-4.879H266.5v-2h157.432l-8.672-4.878-.872-.49.98-1.744z"/><text id="nextElementSibling" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="276" y="212" fill="#AF6E24">next</tspan> <tspan x="304.8" y="212" fill="#C06334">Element</tspan> <tspan x="355.2" y="212" fill="#AF6E24">Sibling</tspan></text><path id="Line-8" fill="#C06334" fill-rule="nonzero" d="M23.631 218.388l.98 1.743-.87.49-8.674 4.879H169v2H15.067l8.673 4.878.872.49-.98 1.744-.872-.49-12-6.75-1.55-.872 1.55-.872 12-6.75.871-.49z"/><text id="previousElementSibli" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="6" y="212" fill="#AF6E24">previous</tspan> <tspan x="63.6" y="212" fill="#C06334">Element</tspan> <tspan x="114" y="212" fill="#AF6E24">Sibling</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M174.822 248.123l1.055 1.7-.85.527-48.476 30.089-6.917 4.292 9.941-.428 1-.043.085 1.998-.999.043-13.755.594-1.776.076.857-1.557 6.636-12.064.482-.876 1.752.964-.482.876-4.797 8.718 6.918-4.293 48.477-30.089.85-.527z"/><path id="Line-3" fill="#C06334" fill-rule="nonzero" d="M269.214 248.115l.835.55 46.157 30.354 6.78 4.46-4.565-8.841-.459-.889 1.777-.918.459.889 6.317 12.233.816 1.58-1.774-.123-13.735-.954-.997-.07.138-1.995.998.07 9.926.689-6.78-4.46-46.156-30.354-.836-.55 1.099-1.671z"/><text id="children" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="188" y="273">children</tspan></text><text id="firstElementChild--" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="43" y="307" fill="#AF6E24">first</tspan> <tspan x="79" y="307" fill="#C06334">Element</tspan> <tspan x="129.4" y="307" fill="#AF6E24">Child </tspan></text><text id="lastElementChild" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="273" y="307" fill="#AF6E24">last</tspan> <tspan x="301.8" y="307" fill="#C06334">Element</tspan> <tspan x="352.2" y="307" fill="#AF6E24">Child</tspan></text><path id="Line-Copy-2" fill="#C06334" fill-rule="nonzero" d="M222.5 151.5v4h-2v-4h2zm0-6v4h-2v-4h2zm0-6v4h-2v-4h2zm-1-14.29l.872 1.55 6.75 12 .49.871-1.743.98-.49-.87-4.88-8.676.001.435h-2l-.001-.432-4.877 8.672-.49.872-1.744-.98.49-.872 6.75-12 .872-1.55zm1 8.29v4h-2v-4h2z"/><path id="Line-2-Copy" fill="#C06334" fill-rule="nonzero" d="M221.5 44.71l.872 1.55 6.75 12 .49.871-1.743.98-.49-.87-4.879-8.674v21.528h-2V50.567l-4.878 8.673-.49.872-1.744-.98.49-.872 6.75-12 .872-1.55z"/></g></g></svg> \ No newline at end of file diff --git a/2-ui/1-document/03-dom-navigation/dom-links-elements@2x.png b/2-ui/1-document/03-dom-navigation/dom-links-elements@2x.png deleted file mode 100644 index cf4e220cf8..0000000000 Binary files a/2-ui/1-document/03-dom-navigation/dom-links-elements@2x.png and /dev/null differ diff --git a/2-ui/1-document/03-dom-navigation/dom-links.png b/2-ui/1-document/03-dom-navigation/dom-links.png deleted file mode 100644 index 25b25b377b..0000000000 Binary files a/2-ui/1-document/03-dom-navigation/dom-links.png and /dev/null differ diff --git a/2-ui/1-document/03-dom-navigation/dom-links.svg b/2-ui/1-document/03-dom-navigation/dom-links.svg new file mode 100644 index 0000000000..6c34bca4a4 --- /dev/null +++ b/2-ui/1-document/03-dom-navigation/dom-links.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="420" height="388" viewBox="0 0 420 388"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="dom-links.svg"><path id="Rectangle-9" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M150 20h117v28H150z"/><path id="Rectangle-7" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M151 154h117v28H151z"/><path id="Rectangle-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M117 87h198v28H117z"/><text id="document" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="179.7" y="38">document</tspan></text><text id="document.documentEle" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="129.6" y="105">document.documentElement </tspan></text><text id="Type-something" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="324.9" y="105"><HTML></tspan></text><text id="document.body--" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="164.2" y="173">document.body </tspan></text><text id="(if-inside-body)" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="283.9" y="173">(if inside body)</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M209.5 119.71l.872 1.55 6.75 12 .49.871-1.743.98-.49-.87-4.879-8.674v23.785h-2v-23.785l-4.878 8.673-.49.872-1.744-.98.49-.872 6.75-12 .872-1.55z"/><path id="Line-2" fill="#C06334" fill-rule="nonzero" d="M209.5 52.71l.872 1.55 6.75 12 .49.871-1.743.98-.49-.87-4.879-8.674v21.528h-2V58.567l-4.878 8.673-.49.872-1.744-.98.49-.872 6.75-12 .872-1.55z"/><path id="Line" stroke="#AF6E24" stroke-linecap="square" stroke-width="2" d="M2.5 191H415"/><text id="parentNode" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="172" y="248">parentNode</tspan></text><path id="Rectangle-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M169 289h80v28h-80z"/><text id="<DIV>" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="12" font-weight="normal"><tspan x="192.192" y="308"><DIV></tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M208.5 254.21l.872 1.55 6.75 12 .49.871-1.743.98-.49-.87-4.88-8.676.001 8.185v16.25h-2v-16.25l-.001-8.182-4.877 8.672-.49.872-1.744-.98.49-.872 6.75-12 .872-1.55z"/><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M209.5 226.5v4h-2v-4h2zm0-6v4h-2v-4h2zm0-6v4h-2v-4h2zm-1-14.29l.872 1.55 6.75 12 .49.871-1.743.98-.49-.87-4.88-8.676.001.435h-2l-.001-.432-4.877 8.672-.49.872-1.744-.98.49-.872 6.75-12 .872-1.55zm1 8.29v4h-2v-4h2z"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M356.369 294.388l.871.49 12 6.75 1.55.872-1.55.872-12 6.75-.871.49-.98-1.743.87-.49 8.673-4.879H254.5v-2h110.432l-8.672-4.878-.872-.49.98-1.744z"/><text id="nextSibling" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="264" y="288">nextSibling</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M61.631 294.388l.98 1.743-.87.49-8.674 4.879H165.5v2H53.067l8.673 4.878.872.49-.98 1.744-.872-.49-12-6.75-1.55-.872 1.55-.872 12-6.75.871-.49z"/><text id="previousSibling" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="50" y="288">previousSibling</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M162.822 324.123l1.055 1.7-.85.527-48.476 30.089-6.917 4.292 9.941-.428 1-.043.085 1.998-.999.043-13.755.594-1.776.076.857-1.557 6.636-12.064.482-.876 1.752.964-.482.876-4.797 8.718 6.918-4.293 48.477-30.089.85-.527z"/><path id="Line-3" fill="#C06334" fill-rule="nonzero" d="M257.214 324.115l.835.55 46.157 30.354 6.78 4.46-4.565-8.841-.459-.889 1.777-.918.459.889 6.317 12.233.816 1.58-1.774-.123-13.735-.954-.997-.07.138-1.995.998.07 9.926.689-6.78-4.46-46.156-30.354-.836-.55 1.099-1.671z"/><text id="childNodes" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="170" y="343">childNodes</tspan></text><text id="firstChild--" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="64" y="379">firstChild </tspan></text><text id="Type-something" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="279" y="379">lastChild</tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/1-document/03-dom-navigation/dom-links@2x.png b/2-ui/1-document/03-dom-navigation/dom-links@2x.png deleted file mode 100644 index 84cd33d4e5..0000000000 Binary files a/2-ui/1-document/03-dom-navigation/dom-links@2x.png and /dev/null differ diff --git a/2-ui/1-document/04-searching-elements-dom/1-find-elements/task.md b/2-ui/1-document/04-searching-elements-dom/1-find-elements/task.md index 2e39fc3e64..786723f383 100644 --- a/2-ui/1-document/04-searching-elements-dom/1-find-elements/task.md +++ b/2-ui/1-document/04-searching-elements-dom/1-find-elements/task.md @@ -9,7 +9,7 @@ importance: 4 どうやって見つけますか? 1. `id="age-table"` を持つテーブル. -2. テーブル内のすべての `label` 要素(3つ存在するはずです) +2. そのテーブル内のすべての `label` 要素(3つ存在するはずです) 3. そのテーブル内の最初の `td` ("Age" という言葉を持つ) 4. 名前が `search` の `form` 5. そのフォーム内の最初の `input` diff --git a/2-ui/1-document/04-searching-elements-dom/article.md b/2-ui/1-document/04-searching-elements-dom/article.md index c8f1e71efe..023b5498b6 100644 --- a/2-ui/1-document/04-searching-elements-dom/article.md +++ b/2-ui/1-document/04-searching-elements-dom/article.md @@ -1,16 +1,32 @@ # 検索: getElement* と querySelector* -要素が互いに近いとき、DOM ナビゲーションプロパティは優れています。仮にそうではなければどうしますか?ページの任意のページはどうすれば取得できるでしょうか? +要素が互いに近い場合には DOM ナビゲーションプロパティは役立ちます。そうではない場合、ページの任意の要素はどのように取得できるでしょうか? そのための追加の検索メソッドがあります。 -[cut] - ## document.getElementById もしくは 単に id -もし要素が `id` 属性を持っている場合、その `id` の名前のグローバル変数があります。 +要素に `id` 属性があると、`document.getElementById(id)` メソッドを使用することで要素が取得できます。 + +例: + +```html run +<div id="elem"> + <div id="elem-content">Element</div> +</div> + +<script> + // 要素を取得 +*!* + let elem = document.getElementById('elem'); +*/!* + + // 背景を赤にします + elem.style.background = 'red'; +</script> +``` -次にように、その要素にアクセスするためにそれを使うことができます。: +また、要素を参照する `id` で名前付けされたグローバル変数もあります: ```html run <div id="*!*elem*/!*"> @@ -18,16 +34,15 @@ </div> <script> - alert(elem); // DOM-element with id="elem" - alert(window.elem); // このようなグローバル変数へのアクセスもできます + // elem は id="elem" の DOM 要素を参照します + elem.style.background = 'red'; - // elem-content の場合は少し複雑です - // ダッシュを含んでいるため、変数名になれません - alert(window['elem-content']); // ...しかし、角括弧 [...] を使うことでアクセス可能です + // id="elem-content" はハイフンがあるので変数名にはできません。 + // が、角括弧を使用してアクセスできます: window['elem-content'] </script> ``` -同じ名前の変数を私達自身で宣言しない限り、アクセスできます: +同じ名前の JavaScript 変数を宣言しない限り、この値が優先されます: ```html run untrusted height=0 <div id="elem"></div> @@ -39,55 +54,139 @@ </script> ``` -この振る舞いは [仕様書](http://www.whatwg.org/specs/web-apps/current-work/#dom-window-nameditem) で説明されていますが、主に互換性のためにサポートされています。ブラウザは JS と DOM の名前空間を混在させることで私達を助けようとします。非常にシンプルなスクリプトに対しては適していますが、名前の衝突が起きる可能性があります。また、JS を見て、ビューの中のHTMLを見なかった場合、変数がどこから来たのかが明白ではありません。 +```warn header="要素にアクセスするのに、id名のグローバル変数は使わないでください" +この振る舞いは [スペック](http://www.whatwg.org/specs/web-apps/current-work/#dom-window-nameditem) で説明されているので、標準の1つです。が、主に互換性のためにサポートされています。 -より良い代替の方法は特別なメソッド `document.getElementById(id)` を使うことです。 +ブラウザは JS と DOM の名前空間を混在させることで助けようとします。シンプルなスクリプトであればよいですが、一般的には良いことではありません。名前が衝突する可能性があります。また、JS を見て、HTMLを見なかった場合、変数がどこから来たのかが明白ではありません。 -例: +このチュートリアルでは、要素を直接参照するために `id` を頻繁に使います。が、それは物事を短く保つためだけです。 -```html run -<div id="elem"> - <div id="elem-content">Element</div> -</div> +実際には、`document.getElementById` が好ましい方法です。 +``` +```smart header="`id` はユニークでなければなりません" +`id` は一意でなければなりません。与えられた `id` の要素はドキュメント上で1つだけです。 + +同じ `id` を持つ要素が複数ある場合、対応するメソッドの振る舞いは予測できません。例えば、`document.getElementById` はランダムで該当する要素のうち1つを返すかもしれません。そのため、`id` は一意となるよう注意してください。 +``` + +```warn header="`document.getElementById` はありますが、`anyNode.getElementById` はありません" +メソッド `getElementById` は `document` オブジェクトに対してのみ呼び出し可能です。これはドキュメント全体から指定された `id` を探します。 +``` + +## querySelectorAll [#querySelectorAll] + +最も用途の広いメソッド `elem.querySelectorAll(css)` は、指定された CSS セレクターに一致する `elem` 内のすべての要素を返します。 + +以下では、最後の子要素となるすべての `<li>` 要素を探します: + +```html run +<ul> + <li>The</li> + <li>test</li> +</ul> +<ul> + <li>has</li> + <li>passed</li> +</ul> <script> *!* - let elem = document.getElementById('elem'); + let elements = document.querySelectorAll('ul > li:last-child'); */!* - elem.style.background = 'red'; + for (let elem of elements) { + alert(elem.innerHTML); // "test", "passed" + } </script> ``` -このチュートリアルでは、要素を直接参照するために `id` を頻繁に使います。が、それは物事を短く保つためだけです。実際には、`document.getElementById` が好ましい方法です。 +任意の CSS セレクタが書けるのでこのメソッドは強力です。 -```smart header="1つだけ存在することができます" -`id` は一意でなければなりません。与えられた `id` の要素はドキュメント上で1つだけ存在することができます。 +```smart header="疑似クラスも使えます" +`:hover` や `:active` のような CSS セレクタの擬似クラスもサポートされています。例えば、`document.querySelectorAll(':hover')` はポインタがある要素のコレクションを返します(ネスト順は、最も外側の `<html>` から最もネストされたものの順です)。 +``` + +## querySelector [#querySelector] + +`elem.querySelector(css)` は、指定された CSS セレクタの最初の要素を返します。 + +つまり、`elem.querySelectorAll(css)[0]` と同じ結果になりますが、後者は *すべての* 要素を探し1つを返す、一方、`elem.querySelector` は1つだけを探します。なので、処理も速く、記述もより短くなります。 + +## matches +これまでのメソッドは DOM を検索していました。 + +[elem.matches(css)](http://dom.spec.whatwg.org/#dom-element-matches) は何も探しません。これは単に `elem` が与えられた CSS セレクタに一致するかをチェックし、`true` または `false` を返します。 + +このメソッドは、要素(配列か何か)を反復処理し、興味のあるものをフィルタしようとするときに便利です。 + +例えば: + +```html run +<a href="http://example.com/file.zip">...</a> +<a href="http://ya.ru">...</a> -もし同じ `id` を持つ複数の要素がある場合、対応するメソッドの振る舞いは予測できないものになります。ブラウザはランダムにそれらのうちの1つを返すかもしれません。なので、ルールを心にとめ、`id` を一意に保ってください。 +<script> + // document.body.children の代わりに任意のコレクションが可能です + for (let elem of document.body.children) { +*!* + if (elem.matches('a[href$="zip"]')) { +*/!* + alert("The archive reference: " + elem.href ); + } + } +</script> ``` -```warn header="`document.getElementById` だけです。 `anyNode.getElementById` はありません" -`document` オブジェクトに対してのみ呼び出すことができるメソッド `getElementById` です。これはドキュメント全体から指定された `id` を探します。 +## closest + +要素の *祖先* は、要素の親、親の親、さらにその親などです。祖先は、該当要素から最上部までの親のチェーンを形成します。 + +メソッド `elem.closest(css)` は CSS セレクタにマッチする最も近い祖先を見ます。`elem` 自身も検索対象に含まれています。 + +つまり、メソッド `closest` は要素(`elem`)から上に進み、親をチェックしていきます。セレクタにマッチしたら、検索を止めその祖先を返却します。 + +例: + +```html run +<h1>Contents</h1> + +<div class="contents"> + <ul class="book"> + <li class="chapter">Chapter 1</li> + <li class="chapter">Chapter 1</li> + </ul> +</div> + +<script> + let chapter = document.querySelector('.chapter'); // LI + + alert(chapter.closest('.book')); // UL + alert(chapter.closest('.contents')); // DIV + + alert(chapter.closest('h1')); // null (h1 は祖先ではないので) +</script> ``` ## getElementsBy* -ノードを探す他のメソッドもあります: +他にもタグやクラスなどでノードを探すメソッドがあります。 -- `elem.getElementsByTagName(tag)` は指定されたタグの要素を探し、そのコレクションを返します。`tag` パラメータは "任意のタグ" としてアスタリスク `"*"` にできます。 +`querySelector` のほうがより強力で、短く書くこともできるため、今日、それらはほとんど歴史的な背景により存在するメソッドとなっています。 -例: +そのため、ここでは主に完全を期すためにそれらを説明します。古いスクリプトではそらを目にすることがあるかもしれません。 +- `elem.getElementsByTagName(tag)` は指定されたタグの要素を探し、そのコレクションを返します。`tag` パラメータは "任意のタグ" としてアスタリスク `"*"` が指定可能です。 +- `elem.getElementsByClassName(className)` は指定された CSS クラスを持つ要素を返します。 +- `document.getElementsByName(name)` は指定された `name` 属性を持つ要素をドキュメント全体で返します。めったに使われません。 + +例: ```js // ドキュメント上のすべての div を取得 let divs = document.getElementsByTagName('div'); ``` -このメソッドは任意の DOM 要素のコンテキストで呼び出し可能です。 - -テーブルの中のすべての `input` を見つけましょう: +テーブル内のすべての `input` タグを見つけましょう: ```html run height=50 <table id="table"> @@ -126,16 +225,16 @@ let divs = document.getElementsByTagName('div'); ``` ````warn header="要素ではなくコレクションを返します!" -別に広く知られている初心者の間違いはこのように書くことです: +もう1つ、広く知られている初心者の間違いはこのように書くことです: ```js // 動作しません document.getElementsByTagName('input').value = 5; ``` -これは動作しません。なぜならそれは入力のコレクションを取り、その中の要素ではなくコレクションに値を代入するためです。 +これは動作しません。なぜならそれは input のコレクションを取得し、その中の要素ではなくコレクション自体に値を代入するためです。 -コレクションを反復するか、数値で要素を取得するかを行い、それから代入する必要があります。: +コレクションを反復するか、数値で要素を取得するかを行った後に代入します。: ```js // 動作します(input がある場合) @@ -143,12 +242,7 @@ document.getElementsByTagName('input')[0].value = 5; ``` ```` -この種類のほとんど使われないメソッドもあります: - -- `elem.getElementsByClassName(className)` は指定された CSS クラスを持つ要素を返します。要素は他のクラスも持っている可能性があります。 -- `document.getElementsByName(name)` は指定された `name` 属性を持つ要素をドキュメント全体で返します。歴史的な理由から存在しますが、極稀にしか使われません。ここでは、完全性のためだけに言及します。 - -例: +`.article` 要素を探します: ```html run height=50 <form name="my-form"> @@ -166,108 +260,9 @@ document.getElementsByTagName('input')[0].value = 5; </script> ``` -## querySelectorAll - -今度は重い大砲に進みます。 - -`elem.querySelectorAll(css)` の呼び出しは、指定された CSS セレクタにマッチする `elem` 内のすべての要素を返します。これは最もよく使われ、強力なメソッドです。 - -ここでは最後の子要素であるすべての `<li>` 要素を探します: - -```html run -<ul> - <li>The</li> - <li>test</li> -</ul> -<ul> - <li>has</li> - <li>passed</li> -</ul> -<script> -*!* - let elements = document.querySelectorAll('ul > li:last-child'); -*/!* - - for (let elem of elements) { - alert(elem.innerHTML); // "test", "passed" - } -</script> -``` - -任意の CSS セレクタを使うことができるので、このメソッドは確かに強力です。 - -```smart header="疑似クラスも同様に使えます" -`:hover` や `:active` のような CSS セレクタの擬似クラスもサポートされています。例えば、`document.querySelectorAll(':hover')` はポインタが現在の要素を含むコレクションを返します(入れ子の順序で: 最も外側の `<html>`から最もネストされたものまで)。 -``` - - -## querySelector - -`elem.querySelector(css)` への呼び出しは指定された CSS セレクタの最初の要素を返します。 - -つまり、`elem.querySelectorAll(css)[0]` と同じ結果になりますが、後者は *すべての* 要素を探し1つをピックアップする一方、`elem.querySelector` は単に1つだけを探します。なので、書くのがより速く短いです。 - -## matches - -以前のメソッドは DOM を検索していました。 - -[elem.matches(css)](http://dom.spec.whatwg.org/#dom-element-matches) は何も探しません。それは単に `elem` が与えられた CSS セレクタに一致するかをチェックします。それは `true` または `false` を返します。 - -このメソッドは、要素(配列か何か)を反復処理し、私たちが興味のあるものをフィルタリングしようとするときに便利です。 - -例えば: - -```html run -<a href="http://example.com/file.zip">...</a> -<a href="http://ya.ru">...</a> - -<script> - // document.body.children の代わりに任意のコレクションが可能です - for (let elem of document.body.children) { -*!* - if (elem.matches('a[href$="zip"]')) { -*/!* - alert("The archive reference: " + elem.href ); - } - } -</script> -``` - -## closest - -与えられた要素の直接上にあるすべての要素は、*祖先* と呼ばれます。 +## ライブ(動的)なコレクション -言い換えると、祖先は: 親、親の親、その親などです。祖先は共に要素から頂点までの親の連鎖を形成します。 - -メソッド `elem.closest(css)` は CSS セレクタににマッチする最も近い祖先を見ます。`elem` 自身も検索に含まれています。 - -つまり、メソッド `closest` は要素から上に進み、各親をチェックします。もしセレクタにマッチしたら、検索を止め、その祖先が返却されます。 - -例: - -```html run -<h1>Contents</h1> - -<div class="contents"> - <ul class="book"> - <li class="chapter">Chapter 1</li> - <li class="chapter">Chapter 1</li> - </ul> -</div> - -<script> - let chapter = document.querySelector('.chapter'); // LI - - alert(chapter.closest('.book')); // UL - alert(chapter.closest('.contents')); // DIV - - alert(chapter.closest('h1')); // null (h1 は祖先ではないので) -</script> -``` - -## Live collections - -すべての `"getElementsBy*"` メソッドは *ライブの* コレクションを返します。このようなコレクションは常にドキュメントの現在の状態を反映し、変更があったとき "自動更新" されます。 +すべての `"getElementsBy*"` メソッドは *ライブ(動的)な* コレクションを返します。このようなコレクションは常にドキュメントの現在の状態を反映し、変更があったとき "自動更新" されます。 下の例では、2つのスクリプトがあります。 @@ -291,9 +286,10 @@ document.getElementsByTagName('input')[0].value = 5; </script> ``` -対象的に、`querySelectorAll` は *静的な* コレクションです。これは固定要素配列です。 +対照的に、`querySelectorAll` は *静的な* コレクションです。これは固定要素配列です。 + +代わりにこれを使った場合、両方のスクリプトは `1` を出力します: -我々が代わりにこれを使った場合、両方のスクリプトは `1` を出力します: ```html run <div>First div</div> @@ -314,8 +310,6 @@ document.getElementsByTagName('input')[0].value = 5; 今その違いを簡単に見ることができます。静的なコレクションは、ドキュメント上に新たな `div` の登場の後も増加しませんでした。 -ここでは要素の追加がコレクションにどう影響するかを示すために別々のスクリプトを使いましたが、任意の DOM 操作がそれらに影響します。すぐ後にそれらについてより見ていきます。 - ## サマリ DOM でノードを検索するための6つの主なメソッドがあります: @@ -326,7 +320,7 @@ DOM でノードを検索するための6つの主なメソッドがあります <td>メソッド</td> <td>何で検索するか</td> <td>要素上で呼ぶことが可能?</td> -<td>ライブ?</td> +<td>ライブ(動的)?</td> </tr> </thead> <tbody> @@ -369,14 +363,12 @@ DOM でノードを検索するための6つの主なメソッドがあります </tbody> </table> -メソッド `getElementById` と `getElementsByName` はドキュメントのコンテキストの場合にだけ呼び出すことができることに注意してください: `document.getElementById(...)`。要素上ではできません: `elem.getElementById(...)` はエラーになります。 - -他のメソッドは、要素上でも呼び出し可能です。例えば、 `elem.querySelectorAll(...)` `elem` の内側を検索します(DOM サブツリーの中)。 +最も使われるのは `querySelector` と `querySelectorAll` ですが、`getElement(s)By*` はたまに役立つか、古いスクリプトに含まれています。 -それ以外に: +その他: - `elem` が指定された CSS セレクタに一致するかをチェックする `elem.matches(css)` があります。 -- 指定された CSS セレクタに一致する最も近い祖先を探すための `elem.closest(css)` があります。`elem` 自身もまたチェックされます。 +- 指定された CSS セレクタに一致する最も近い祖先を探すための `elem.closest(css)` があります。`elem` 自身もチェックされます。 また、親子の関係を調べるもう一つの方法をここで言及しましょう: - `elemA.contains(elemB)` は `elemB` が `elemA` の中にある(`elemA` の子孫)または `elemA==elemB` の場合 true を返します。 diff --git a/2-ui/1-document/04-searching-elements-dom/2-tree-info/solution.md b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.md similarity index 100% rename from 2-ui/1-document/04-searching-elements-dom/2-tree-info/solution.md rename to 2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.md diff --git a/2-ui/1-document/04-searching-elements-dom/2-tree-info/solution.view/index.html b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.view/index.html similarity index 100% rename from 2-ui/1-document/04-searching-elements-dom/2-tree-info/solution.view/index.html rename to 2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.view/index.html diff --git a/2-ui/1-document/04-searching-elements-dom/2-tree-info/source.view/index.html b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/source.view/index.html similarity index 100% rename from 2-ui/1-document/04-searching-elements-dom/2-tree-info/source.view/index.html rename to 2-ui/1-document/05-basic-dom-node-properties/2-tree-info/source.view/index.html diff --git a/2-ui/1-document/04-searching-elements-dom/2-tree-info/task.md b/2-ui/1-document/05-basic-dom-node-properties/2-tree-info/task.md similarity index 100% rename from 2-ui/1-document/04-searching-elements-dom/2-tree-info/task.md rename to 2-ui/1-document/05-basic-dom-node-properties/2-tree-info/task.md diff --git a/2-ui/1-document/05-basic-dom-node-properties/article.md b/2-ui/1-document/05-basic-dom-node-properties/article.md index 4717949d74..5faebc8a3a 100644 --- a/2-ui/1-document/05-basic-dom-node-properties/article.md +++ b/2-ui/1-document/05-basic-dom-node-properties/article.md @@ -2,13 +2,11 @@ DOM ノードをより深く見ていきましょう。 -このチャプターでは、それらが何者であるか、最もよく使われるプロパティについて詳しく見ていきます。 - -[cut] +この章ではそれらが何者であるか、そしてよく使われるプロパティについて見ていきます。 ## DOM ノードクラス -DOM ノードはそれらのクラスに応じて異なるプロパティを持っています。例えば、タグ `<a>` に対応する要素ノードはリンク関連のプロパティを持っており、`<input>` に対応する要素ノードは入力関連のプロパティを持っています。テキストノードは要素ノードとは違います。しかし、すべての DOM ノードのクラスは単一の階層を形成するため、すべてのノードの間で共通のプロパティやメソッドを持っています。 +異なる DOM ノードは異なるプロパティを持ちます。例えば、タグ `<a>` に対応する要素ノードはリンク関連のプロパティを持っており、`<input>` に対応する要素ノードは入力関連のプロパティを持っています。テキストノードは要素ノードとは違いますが、すべての DOM ノードのクラスは1つのの階層を形成するため、すべてのノードで共通のプロパティやメソッドがあります。 各 DOM ノードは対応する組み込みクラスに属しています。 @@ -16,22 +14,26 @@ DOM ノードはそれらのクラスに応じて異なるプロパティを持 ここに、図と説明があります: -![](dom-class-hierarchy.png) +![](dom-class-hierarchy.svg) -The classes are: +クラスは次の通りです: -- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) -- はルートの "抽象" クラスです。そのクラスのオブジェクトは決して生成されません。これはベースとして機能し、すべての DOM ノードはいわゆる "イベント" をサポートします。イベントについては後ほど学びます。 -- [Node](http://dom.spec.whatwg.org/#interface-node) -- もまた "抽象" クラスで、DOM ノードの基底として機能します。これはコアなツリー機能を提供します。: `parentNode`, `nextSibling`, `childNodes` など(これらは getter です)。`Node` クラスのオブジェクトは決して生成されません。しかし、それを継承した具体的なノードクラス、即ち: テキストノードの `Text`, 要素ノードの `Element` やコメントノードの `Comment` のようによりエキゾチックなものもあります。 -- [Element](http://dom.spec.whatwg.org/#interface-element) -- は DOM 要素のベースクラスです。それは、`nextElementSibling`, `children` や `getElementsByTagName`, `querySelector` のような検索メソッドといった要素レベルのナビゲーションを提供します。ブラウザでは、HTML だけでなく、XML や SVG のドキュメントがある場合もあります。`Element` クラスは ` SVGElement`、`XMLElement`、`HTMLElement` といったより具体的なクラスのベースとして機能します。 +- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) -- はルートの "抽象" クラスです。このクラスのオブジェクトは生成されません。これはベースとして機能し、すべての DOM ノードはいわゆる "イベント" をサポートします。イベントについては後ほど学びます。 +- [Node](http://dom.spec.whatwg.org/#interface-node) -- もまた "抽象" クラスで、DOM ノードの基底として機能します。これはコアなツリー機能を提供します。: `parentNode`, `nextSibling`, `childNodes` など(これらは getter です)。`Node` クラスのオブジェクトは決して生成されません。しかし、それを継承した具体的なノードクラス、即ち: テキストノードの `Text`, 要素ノードの `Element` や、コメントノードのための `Comment` などもあります。 +- [Element](http://dom.spec.whatwg.org/#interface-element) -- は DOM 要素のベースクラスです。`nextElementSibling`, `children` や `getElementsByTagName`, `querySelector` 等の検索メソッドといった、要素レベルのナビゲーションを提供します。ブラウザでは、HTML だけでなく、XML や SVG のドキュメントがある場合もあります。`Element` クラスは ` SVGElement`、`XMLElement`、`HTMLElement` といったより具体的なクラスのベースとして機能します。 - [HTMLElement](https://html.spec.whatwg.org/multipage/dom.html#htmlelement) -- は最終的にすべてのHTML要素のベースクラスです。色々な HTML要素 がこれを継承しています。: - [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) -- `<input>` 要素のためのクラス, - [HTMLBodyElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlbodyelement) -- `<body>` 要素のためのクラス, - [HTMLAnchorElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlanchorelement) -- `<a>` 要素のためのクラス - ...など、各タグは固有のプロパティやメソッドを提供する独自のクラスを持っています。 -従って、指定されたノードのプロパティとメソッドのフルセットは継承の結果として得られます。 +`<span>`, `<section>`, `<article>` のようなプロパティは固有のプロパティを持たない一方で、固有のプロパティやメソッドをもつ独自のクラスを持つ他の多くのタグがあります。 + +そのため、指定されたノードのプロパティとメソッドのフルセットは継承の結果として得られます。 -例えば、`<input>` 要素の DOM オブジェクトを考えてみましょう。これは [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) クラスに属しています。それは、次の重ね合わせとしてプロパティとメソッドを取得します。: +例えば、`<input>` 要素の DOM オブジェクトを考えてみましょう。これは [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) クラスに属しています。 + +それは、次の重ね合わせとしてプロパティとメソッドを取得します(継承順でリストしています): - `HTMLInputElement` -- このクラスは入力固有のプロパティを提供し、次を継承しています。 - `HTMLElement` -- 共通のHTML要素メソッド (とgetter/setter)を提供し、次を継承しています。 @@ -67,7 +69,7 @@ alert( document.body instanceof EventTarget ); // true ブラウザ、`console.dir(elem)` を使った要素の出力でも簡単に見ることができます。コンソールでは、`HTMLElement.prototype`, `Element.prototype` などを見ることができます。 ```smart header="`console.dir(elem)` VS `console.log(elem)`" -ほとんどのブラウザは、その開発者ツールで2つのコマンドをサポートしています。`console.log` と `console.dir` です。それらは引数をコンソールに出力します。JavaScript オブジェクトに対して、それらは通常同じ動作をします。 +ほとんどのブラウザは、開発者ツールで2つのコマンドをサポートしています。`console.log` と `console.dir` です。それらは引数をコンソールに出力します。JavaScript オブジェクトに対しては通常同じ動作をします。 しかし、DOM 要素に対しては異なります。: @@ -78,7 +80,7 @@ alert( document.body instanceof EventTarget ); // true ``` ````smart header="仕様での IDL" -仕様では、クラスは JavaScript ではなく、特別な [Interface description language](https://en.wikipedia.org/wiki/Interface_description_language) (IDL) を使って説明されています。これは通常理解しやすいです。 +スペックでは、クラスは JavaScript ではなく、特別な [Interface description language](https://en.wikipedia.org/wiki/Interface_description_language) (IDL) を使って説明されています。これは理解しやすいです。 IDL では、すべてのプロパティの型が前についています。例えば `DOMString`, `boolean` などです。 @@ -112,19 +114,17 @@ interface HTMLInputElement: HTMLElement { ... } ``` - -他のクラスもいくらか似ています。 ```` ## "nodeType" プロパティ -`nodeType` プロパティは DOM ノードの "タイプ" を取得する昔ながらの方法を提供します。 +`nodeType` プロパティは DOM ノードの "タイプ" を取得する昔ながらの方法です。 -それは次の数値を持っています: +次の数値を持っています: - `elem.nodeType == 1` は要素ノード, - `elem.nodeType == 3` はテキストノード, - `elem.nodeType == 9` はドキュメントオブジェクト, -- [仕様](https://dom.spec.whatwg.org/#node) ではもういくつかの値があります。 +- [スペック](https://dom.spec.whatwg.org/#node) ではもういくつかの値があります。 例えば: @@ -145,7 +145,7 @@ interface HTMLInputElement: HTMLElement { </body> ``` -現代のスクリプトでは、`instanceof` と他のクラスベースのテストを使ってノードタイプを見ることができますが、`nodeType` の方がシンプルなときもあります。`nodeType` は参照のみで変更はできません。 +現在のスクリプトでは、`instanceof` と他のクラスベースのテストを使ってノードタイプを見ることができますが、`nodeType` の方がシンプルなときもあります。`nodeType` は参照のみで変更はできません。 ## タグ: nodeName と tagName @@ -158,7 +158,7 @@ alert( document.body.nodeName ); // BODY alert( document.body.tagName ); // BODY ``` -tagName と nodeName に違いはあるでしょうか? +tagName と nodeName の違いはなにでしょうか? もちろん、その違いはそれらの名前に反映されていますが、実際は少し微妙です。 @@ -186,10 +186,10 @@ tagName と nodeName に違いはあるでしょうか? </body> ``` -もし要素だけを扱う場合、`tagName` だけを使うべきです。 +要素だけを扱う場合、`tagName` と `nodeName` どちらも利用でき、そこに差はありません。 -```smart header="タグ名は XHTML を除いて常に大文字です" +```smart header="タグ名は XML モードを除いて常に大文字です" ブラウザはドキュメントを処理する2つのモードを持っています。: HTML と XML です。通常webページではHTMLモードが使われます。XMLモードは、ブラウザがヘッダでXMLドキュメントを受け取ったときに有効になります。: `Content-Type: application/xml+xhtml`。 HTMLモードでは、`tagName/nodeName` は常に大文字です: `<body>` または `<BoDy>` は `BODY` です。 @@ -198,13 +198,13 @@ XMLモードでは、文字の大小は "そのまま" 維持されます。最 ``` -## innerHTML: そのコンテンツ +## innerHTML: コンテンツ [innerHTML](https://w3c.github.io/DOM-Parsing/#widl-Element-innerHTML)プロパティは要素内の HTML を文字列として取得することができます。 -我々はそれを変更することもできます。なので、ページを変更する最も強力な方法の1つです。 +それを変更することもできます。なので、ページを変更する最も強力な方法の1つです。 -この例は `document.body` のコンテンツを表示し、その後それを完全に置き換えます: +この例は `document.body` のコンテンツを表示し、その後、コンテンツを完全に置き換えます: ```html run <body> @@ -233,9 +233,7 @@ XMLモードでは、文字の大小は "そのまま" 維持されます。最 ``` ```smart header="スクリプトは実行しません" -もし `innerHTML` が `<script>` タグをドキュメントに挿入した場合、-- それは実行しません。 - -それはすでに実行したスクリプトとして HTML の一部になります。 +もし `innerHTML` が `<script>` タグをドキュメントに挿入した場合、-- HMLT の一部にはなりますが、実行されません。 ``` ### 注意: "innerHTML+=" は完全な置き換えをします @@ -266,7 +264,6 @@ elem.innerHTML = elem.innerHTML + "..." 1. 古いコンテンツは削除されます。 2. 代わりに新しい `innerHTML` が書かれます(古いものと新しいものの連結)。 - **コンテンツが "完全に取り除かれ" 、最初から書き直されると、すべての画像やその他のリソースはリロードされます**。 上の `chatDiv` の例では、行 `chatDiv.innerHTML+="How goes?"` は HTML コンテンツを再作成し、 `smile.gif` をリロードします(キャッシュされていることを望みます)。もし `chatDiv` が多くのテキストや画像を持っている場合、リロードがはっきり見えます。 @@ -289,9 +286,9 @@ elem.innerHTML = elem.innerHTML + "..." </script> ``` -**注意: `innerHTML` とは違い、`outerHTML` への書き込みは要素を変更しません。代わりに、それは外部のコンテキストでそれを全体として置き換えます。** +**注意: `innerHTML` とは違い、`outerHTML` への書き込みは要素を変更しません。代わりに、DOM で置き換えられます。** -ええ、奇妙に聞こえ、それは確かに奇妙です。なのでそれについて別々のメモをします。見てみましょう: +ええ、奇妙に聞こえ、それは確かに奇妙です。それについて補足します。見てみましょう: 例を考えてみましょう: @@ -313,23 +310,28 @@ elem.innerHTML = elem.innerHTML + "..." </script> ``` -行 `(*)` では、`<div>...</div>` の完全な HTML を取り、`<p>...</p>` によって置き換えます。外側のドキュメントでは、`<div>` の代わりに新しいコンテンツを見ることができます。しかし、古い `div` 変数は以前として同じです。 +奇妙に見えませんか? + +行 `(*)` では、`<div>` を `<p>A new element</p>` に置き換えます。外側のドキュメント(DOM)では、`<div>` の代わりに新しいコンテンツが見るます。しかし、行 `(**)` では、古い `div` 変数は変更されていません。 -`outerHTML` の代入は DOM 要素を変更するのではなく、外側のコンテキストからそれを抽出し、代わりに新しい HTML の一部を挿入します。 +`outerHTML` の代入は DOM 要素(参照されているオブジェクト、今回のケースでは変数 'div')を変更するのではなく、DOM からそれを削除し、新しいHTMLをその場所へ挿入します。 -初心者の開発者は時々ここで間違いをします: 彼らは `div.outerHTML` を修正し、`div` がまるで新しいコンテンツを持っているかのように処理を続けます。 +なので、`div.outerHTML=...` で起きていることは以下の通りです: +- `div` がドキュメントから削除されます。 +- 別の HTML のピース `<p>A new element</p>` がその場に挿入されます。 +- `div` は古い値を持ったままです。新しいHTMLはどこにも保存されていません。 -それは `innerHTML` では可能ですが、`outerHTML` ではできません。 +`div.outerHTML` を変更し、`div` が新しいコンテンツかのように扱い処理を続けると、すぐにエラーになるでしょう。このようなことは `innerHTML` なら問題ありませんが、`outerHTML` ではうまくいきません。 -私達は `outerHTML` へ書き込みはできますが、書き込んでいる要素は変更されないことについて留意しておくべきです。代わりにその場所に新しいコンテンツを作成します。DOM に問い合わせることで新しい要素を参照することができます。 +`elem.outerHTML` で書き込みはできますが、'elem' に書き込んでいるものは変わらないことについて留意しておく必要があります。代わりにその場所に新しいコンテンツを作成します。DOM に問い合わせることで新しい要素を参照することができます。 ## nodeValue/data: テキストノードのコンテンツ `innerHTML` プロパティは要素ノードに対してのみ有効です。 -他のノードタイプはそれらに対応するものを持っています: `nodeValue` と `data` プロパティです。これら2つは実際の利用ではほとんど同じで、仕様上少し違いがあるだけです。なので、より短い `data` を使います。 +他のノードタイプにはそれに対応するものがあります: `nodeValue` と `data` プロパティです。これら2つは実際の利用においてはほとんど同じで、仕様上少し違いがあるだけです。なので、より短い `data` を使います。 -次のようにして `data` を読むことができます: +テキストノードのコンテンツとコメントを読む例です: ```html run height="50" <body> @@ -349,7 +351,9 @@ elem.innerHTML = elem.innerHTML + "..." </body> ``` -テキストノードに対しては、それらが読めたり修正できる理由が想像できます。しかしなぜコメントも?通常それらは全く興味ありませんが、次のように開発者が HTML の中に情報を埋め込むことがあります。: +テキストノードに対しては、それらが読み込んだり修正できる理由が想像できます。しかしなぜコメントも? + +次のように開発者が HTML の中に情報やテンプレートの説明を埋め込むことがあります。: ```html <!-- if isAdmin --> @@ -377,15 +381,15 @@ elem.innerHTML = elem.innerHTML + "..." </script> ``` -ご覧の通り、すべての `<タグ>` が切り取られたがその中のテキストは残っているような形でテキストのみが返却されます。 +ご覧の通り、すべての `<tags>` が取り除かれ、テキストだけが残る形でテキストのみが返却されます。 実際、このようなテキストを読み込むことはめったにありません。 **`textContent` への書き込みはとても役立ちます。なぜならテキストを "安全な方法" で書くことができるからです。** -例えばユーザによって入力された任意の文字列を持っていて、それを表示したいとしましょう。 +例えばユーザによって入力された任意の文字列を表示したいとしましょう。 -- `innerHTML` を使うと、すべての HTML タグを共に、"HTML として" 挿入されます。 +- `innerHTML` を使うと、すべての HTML タグと一緒に、"HTML として" 挿入されます。 - `textContent` では、すべてのシンボルは文字通り扱われ、"テキスト として" 挿入されます。 2つを比べてみます: @@ -411,7 +415,7 @@ elem.innerHTML = elem.innerHTML + "..." "hidden" 属性と DOM プロパティは要素が見えるかどうかを指定します。 -私たちは次のように HTML の中、もしくは JavaScript を使った代入で使うことができます。: +次のように HTML の中、もしくは JavaScript を使った代入で使用できます。: ```html run height="80" <div>Both divs below are hidden</div> @@ -438,9 +442,9 @@ elem.innerHTML = elem.innerHTML + "..." </script> ``` -## More properties +## その他のプロパティ -DOM 要素は追加のプロパティも持っており、それらの多くはクラスによって提供されています。: +DOM 要素には追加のプロパティ、特にクラスに依存するプロパティもあります: - `value` -- `<input>`, `<select>` や `<textarea>` (`HTMLInputElement`, `HTMLSelectElement`...) のための値。 - `href` -- `<a href="...">` (`HTMLAnchorElement`) の `href`. @@ -459,32 +463,32 @@ DOM 要素は追加のプロパティも持っており、それらの多くは </script> ``` -ほとんどの一般的な HTML 属性は対応する DOM プロパティを持っており、このようにしてアクセスすることができます。 +ほとんどの標準の HTML 属性は対応する DOM プロパティを持っており、このようにしてアクセスすることができます。 -もし指定したクラスに対して、サポートされているすべてのプロパティのリストが知りたい場合、仕様でそれを見つけることができます。例えば、HTMLInputElemen は <https://html.spec.whatwg.org/#htmlinputelement> でドキュメント化されています。 +指定したクラスがサポートされているすべてのプロパティのリストはスペックにあります。例えば、HTMLInputElemen は <https://html.spec.whatwg.org/#htmlinputelement> でドキュメント化されています。 -あるいは、速くそれを知りたい場合や具体的なブラウザで関心がある場合には、-- いつでも `console.dir(elem)` を使って要素を出力しプロパティを読むことができます。もしくはブラウザ開発者ツールの Elements タブで "DOM プロパティ" を参照してください。 +あるいは、速くそれを知りたい場合や、具体的なブラウザでのスペックが知りたい場合には、-- いつでも `console.dir(elem)` で要素を出力しプロパティを確認することができます。もしくはブラウザ開発者ツールの Elements タブで "DOM プロパティ" を参照してください。 ## サマリ 各 DOM ノードは特定のクラスに属します。クラスは階層を形成します。プロパティとメソッドの完全なセットは継承の結果として得られます。 -メインの DOM ノードプロパティは次の通りです: +主な DOM ノードプロパティは次の通りです: `nodeType` -: ノードタイプ。DOM オブジェクトクラスから取得することができますが、それがテキストノードか要素ノードかを単に知りたいだけであることがしばしばです。 `nodeType` プロパティはそれに適しています。これは数値で最も重要なのは: 要素の場合 `1` と、テキストノードの場合 `3` です。読み取り専用です。 +: ノードがテキストノードか要素ノードかが確認できます。要素の場合 `1` と、テキストノードの場合 `3` 、その他いくつか種類があります。読み取り専用です。 `nodeName/tagName` -: 要素の場合、タグ名です(XMLモードを除いて大文字)。非要素ノードの場合、`nodeName` それが何であるかを説明します。読み取り専用。 +: 要素の場合、タグ名です(XMLモードを除き大文字)。非要素ノードの場合、`nodeName` はそれが何かを説明します。読み取り専用です。 `innerHTML` -: 要素のHTMLコンテンツ。変更可能。 +: 要素のHTMLコンテンツ。変更可能です。 `outerHTML` -: 要素の完全なHTMLです。`elem.outerHTML` への書き込み操作は `elem` 自体に触れません。代わりに、外部のコンテキストにおいて、新しいHTMLで置き換えます。 +: 要素の完全なHTMLです。`elem.outerHTML` への書き込み操作は `elem` 自体には触れません。代わりに、外部のコンテキストで新しいHTMLで置き換えます。 `nodeValue/data` -: 非要素ノード(テキスト、コメント)のコンテンツです。これら2つはほとんど同じで通常は `data` を使います。変更可能。 +: 非要素ノード(テキスト、コメント)のコンテンツです。これら2つはほとんど同じで通常は `data` を使います。変更可能です。 `textContent` : 要素中のテキストで、基本的には HTML からすべての `<タグ>` を除いたものです。それに書き込むと、要素内にテキストが配置され、すべての特殊文字とタグがテキストとして正確に扱われます。 ユーザーが作成したテキストを安全に挿入し、不要なHTMLの挿入を防ぐことができます。 @@ -494,4 +498,4 @@ DOM 要素は追加のプロパティも持っており、それらの多くは DOM ノードはクラスに応じて他のプロパティも持っています。例えば `<input>` 要素(`HTMLInputElement`) は `value`, `type` をサポートし、 `<a>` 要素 (`HTMLAnchorElement`) は `href` などです。ほとんどの標準HTML属性は対応する DOM プロパティを持っています。 -しかし、HTML属性とDOMプロパティは必ずしも同じではありません。これについては次のチャプターで説明します。 +しかし、HTML属性とDOMプロパティは必ずしも同じではありません。これについては次の章で説明します。 diff --git a/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy.png b/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy.png deleted file mode 100644 index e48064cce4..0000000000 Binary files a/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy.png and /dev/null differ diff --git a/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy.svg b/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy.svg new file mode 100644 index 0000000000..39f7d8f8c3 --- /dev/null +++ b/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="478" height="364" viewBox="0 0 478 364"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="dom-class-hierarchy.svg"><path id="Rectangle-9" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M181 6h118v28H181z"/><path id="Rectangle-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M181 74h118v28H181z"/><text id="EventTarget" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="200.9" y="24">EventTarget</tspan></text><text id="Node" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="225.6" y="91">Node </tspan></text><path id="Line-2" fill="#C06334" fill-rule="nonzero" d="M240.5 39.5l7 14h-6v17h-2v-17h-6l7-14z"/><path id="Rectangle-8-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M181 144h118v28H181z"/><text id="Element" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="214.8" y="161">Element </tspan></text><path id="Line-2-Copy" fill="#C06334" fill-rule="nonzero" d="M240.5 109.5l7 14h-6v17h-2v-17h-6l7-14z"/><path id="Rectangle-8-Copy-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M181 230h118v28H181z"/><text id="HTMLElement" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="200.4" y="247">HTMLElement </tspan></text><path id="Line-2-Copy-4" fill="#C06334" fill-rule="nonzero" d="M240.5 195.5l7 14h-6v17h-2v-17h-6l7-14z"/><path id="Rectangle-8-Copy-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M171 300h138v28H171z"/><text id="HTMLBodyElement" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="186" y="317">HTMLBodyElement </tspan></text><path id="Line-2-Copy-6" fill="#C06334" fill-rule="nonzero" d="M240.5 265.5l7 14h-6v17h-2v-17h-6l7-14z"/><path id="Rectangle-8-Copy-7" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M12 300h138v28H12z"/><text id="HTMLInputElement" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="24.4" y="317">HTMLInputElement </tspan></text><path id="Line-2-Copy-7" fill="#C06334" fill-rule="nonzero" d="M170 261l-6.753 14.12-3.685-4.736-29.448 22.905-.79.614-1.227-1.578.79-.614 29.448-22.906-3.684-4.735L170 261z"/><path id="Rectangle-8-Copy-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M332 300h138v28H332z"/><text id="HTMLAnchorElement" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="341.3" y="317">HTMLAnchorElement </tspan></text><path id="Line-2-Copy-8" fill="#C06334" fill-rule="nonzero" d="M307 259l14.554 5.76-4.47 4.002 20.661 23.07.667.746-1.49 1.334-.667-.745-20.66-23.071-4.47 4.003L307 259z"/><path id="Rectangle-8-Copy-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M45 126h118v28H45z"/><text id="Text" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="89.6" y="144">Text </tspan></text><path id="Line-2-Copy-2" fill="#C06334" fill-rule="nonzero" d="M171 96l-6.589 14.198-3.738-4.693-18.55 14.777-.782.623-1.246-1.564.782-.623 18.549-14.778-3.738-4.692L171 96z"/><path id="Rectangle-8-Copy-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M315 126h118v28H315z"/><text id="Comment" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="348.8" y="144">Comment </tspan></text><path id="Line-2-Copy-3" fill="#C06334" fill-rule="nonzero" d="M307 96l15.338 3.123-3.701 4.723 18.98 14.867.787.616-1.233 1.575-.788-.617-18.979-14.867-3.7 4.724L307 96z"/><path id="Line-2-Copy-9" fill="#C06334" fill-rule="nonzero" d="M307 174l15.338 3.123-3.701 4.723 18.98 14.867.787.616-1.233 1.575-.788-.617-18.979-14.867-3.7 4.724L307 174z"/><path id="Rectangle-8-Copy-5" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M315 205h118v28H315z"/><text id="SVGElement" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="338" y="223">SVGElement </tspan></text><text id="<div>Text</div>" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="48" y="171" fill="#AF6E24"><div></tspan> <tspan x="84" y="171" fill="#C06334">Text</tspan> <tspan x="112.8" y="171" fill="#AF6E24"></div></tspan></text><text id="<input-type="…">" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="24.4" y="341"><input type="…"></tspan></text><text id="<body>" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="220.4" y="341"><body></tspan></text><text id="<a-href="…">" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="358.8" y="341"><a href="…"></tspan></text><text id="<div>Text</div>" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="189" y="188" fill="#AF6E24"><</tspan> <tspan x="196.2" y="188" fill="#C06334">div</tspan> <tspan x="217.8" y="188" fill="#AF6E24">></tspan> <tspan x="225" y="188" fill="#DBAF88">Text</tspan> <tspan x="253.8" y="188" fill="#AF6E24"></</tspan> <tspan x="268.2" y="188" fill="#C06334">div</tspan> <tspan x="289.8" y="188" fill="#AF6E24">></tspan></text><text id="<!--comment-->" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="327.6" y="171"><!--comment--></tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy@2x.png b/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy@2x.png deleted file mode 100644 index 343f4890d5..0000000000 Binary files a/2-ui/1-document/05-basic-dom-node-properties/dom-class-hierarchy@2x.png and /dev/null differ diff --git a/2-ui/1-document/06-dom-attributes-and-properties/article.md b/2-ui/1-document/06-dom-attributes-and-properties/article.md index 532327125f..9e1b75f5f3 100644 --- a/2-ui/1-document/06-dom-attributes-and-properties/article.md +++ b/2-ui/1-document/06-dom-attributes-and-properties/article.md @@ -1,18 +1,16 @@ # 属性とプロパティ -ブラウザがページをロードするとき、HTMLテキストを "読み" (別の言葉: "パース")、そこから DOM オブジェクトを生成します。要素ノードの場合、ほとんどの標準のHTML属性は自動的に DOM オブジェクトのプロパティになります。 +ブラウザがページをロードするとき、HTMLテキストを "読み込み" (別の言葉: "パース")、DOM オブジェクトを生成します。要素ノードの場合、ほとんどの標準のHTML属性は自動的に DOM オブジェクトのプロパティになります。 例えば、もしタグが `<body id="page">` の場合、DOM オブジェクトは `body.id="page"` を持ちます。 -しかし、属性-プロパティのマッピングは1対1ではありません! このチャプターでは、これら2つの概念を分け、それらが同じであるとき、異なるときにそれらがどのように機能するか、見てみましょう。 - -[cut] +しかし、属性-プロパティのマッピングは1対1ではありません! この章では、これら2つの概念を分け、それらが同じであるとき/異なるときにどのように振る舞うか見ていきます。 ## DOM プロパティ -私たちはすでに組み込みの DOM プロパティを見ました。それらはたくさんありますが、技術的には誰も制限はしておらず、もしそれが十分でなければ -- 我々自身で追加することができます。 +私たちはすでに組み込みの DOM プロパティを学びました。多くのプロパティがありますが、技術的な制限はないため、不十分であれば独自のプロパティが追加できます。 -DOM ノードは通常のJavaScriptオブジェクトです。それを変更することができます。 +DOM ノードは通常のJavaScriptオブジェクトです。それを変更できます。 例えば、`document.body` に新しいプロパティを作成してみましょう: @@ -48,14 +46,14 @@ document.body.sayHi(); // Hello, I'm BODY 従って、DOM プロパティとメソッドは、通常のJavaScriptオブジェクトと同じように振る舞います: -- 任意の値を持つことができる -- 大文字と小文字が区別される(`elem.NoDeTyPe` ではなく、`elem.nodeType` と書きます。) +- 任意の値を持つことができます。 +- 大文字と小文字が区別されます(`elem.NoDeTyPe` ではなく、`elem.nodeType` と書きます。) ## HTML 属性 -HTML 言語では、タグは属性を持つことがあります。ブラウザがHTMLテキストを読み込み、タグに対する DOM オブジェクトを作成するとき、それは *標準の* 属性を認識し、それらから DOM プロパティを生成します。 +HTMLでは、タグが属性を持つことがあります。ブラウザがHTMLをパースして、タグに対する DOM オブジェクトを作成するとき、*標準の* 属性を認識し、DOM プロパティを生成します。 -従って、要素が `id` または別の *標準の* 属性を持っているとき、対応するプロパティは生成されます。しかし、属性が非標準でない場合には、それは起こりません。 +従って、要素が `id` または他の *標準の* 属性を持っている場合、対応するプロパティが生成されます。しかし、属性が非標準の場合はプロパティは生成されません。 例: ```html run @@ -70,9 +68,9 @@ HTML 言語では、タグは属性を持つことがあります。ブラウザ </body> ``` -ある要素に対する標準属性は別の要素にとっては未知になる可能性があることに注意してください。例えば、`"type"` は `<input>` ([HTMLInputElement](https://html.spec.whatwg.org/#htmlinputelement)) では標準ですが、`<body>` ([HTMLBodyElement](https://html.spec.whatwg.org/#htmlbodyelement)) では標準はありません。標準属性は対応する要素クラスの仕様で記述されています。 +ある要素に対する標準属性は、別の要素では未知の属性の可能性があることに注意してください。例えば、`"type"` は `<input>` ([HTMLInputElement](https://html.spec.whatwg.org/#htmlinputelement)) では標準ですが、`<body>` ([HTMLBodyElement](https://html.spec.whatwg.org/#htmlbodyelement)) では標準ではありません。標準属性は対応する要素クラスの仕様で記述されています。 -ここではそれを見ることができます: +ここではそれが確認できます: ```html run <body id="body" type="..."> <input id="input" type="text"> @@ -85,20 +83,20 @@ HTML 言語では、タグは属性を持つことがあります。ブラウザ </body> ``` -なので、もし属性が非標準の場合、それに対する DOM プロパティはありません。このような属性にアクセスする方法はあるでしょうか? +なので、もし属性が非標準の場合は DOM プロパティはありません。このような属性にアクセスする方法はあるでしょうか? -もちろんです。すべての属性は次のメソッドを使ってアクセスすることができます。: +もちろんです。すべての属性は次のメソッドでアクセス可能です。: - `elem.hasAttribute(name)` -- 存在をチェックします。 - `elem.getAttribute(name)` -- 値を取得します。 - `elem.setAttribute(name, value)` -- 値を設定します。 - `elem.removeAttribute(name)` -- 属性を削除します。 -これらのメソッドは HTML で書かれているものと正確に操作します。 +これらのメソッドは HTML で書かれている属性に対して操作します。 -`elem.attributes` を使うことで、すべての属性を読み込むこともできます: 組み込み [Attr](https://dom.spec.whatwg.org/#attr) クラスに属し、 `name` と `value` プロパティをもつ、オブジェクトのコレクションです。 +`elem.attributes` で、すべての属性を読み込むことも可能です: 組み込み [Attr](https://dom.spec.whatwg.org/#attr) クラスに属し、 `name` と `value` プロパティをもつ、オブジェクトのコレクションです。 -これは、非標準のプロパティを読み込む例です: +以下は、非標準のプロパティを読み込む例です: ```html run <body something="non-standard"> @@ -110,12 +108,12 @@ HTML 言語では、タグは属性を持つことがあります。ブラウザ </body> ``` -HTML属性は次の特徴を持っています: +HTML属性には次の特徴があります: -- それらの名前は大文字小文字を区別しません(それはHTMLです: `id` は `ID` と同じです) -- それらは常に文字列です。 +- 名前は大文字小文字を区別しません(`id` は `ID` と同じです) +- 常に文字列です -ここでは、属性を使った動作の拡張デモを紹介します。: +これは、属性を使ったデモです: ```html run <body> @@ -135,18 +133,18 @@ HTML属性は次の特徴を持っています: </body> ``` -注意事項: +補足: -1. `getAttribute('About')` -- 最初の文字はここでは大文字で、HTMLではすべて小文字です。 しかし、それは重要ではありません。: 属性名は大文字と小文字を区別しません。 -2. 属性には何でも割り当てることができますが、それは文字列になります。 だからここでは値として `"123"` があります。 -3. 私たちが設定したものを含むすべての属性は `outerHTML` で見えます。 +1. `getAttribute('About')` -- 1文字目は大文字の一方、HTML上では小文字ですが、関係ありません。: 属性名は大文字と小文字を区別しません。 +2. 属性には何でも代入できますが、文字列になります。 そのため、ここでは値 `"123"` です。 +3. 設定したものを含む全属性は `outerHTML` で確認できます。 4. `attributes` のコレクションは反復可能で、属性は `name` と `value` を持っています。 ## プロパティ-属性 の同期 標準の属性を変更するとき、対応するプロパティは自動更新され、(いくつかの例外を除いては)逆もまた然りです。 -下の例では、`id` は属性として修正され、我々はそのプロパティの変更も見ることができます。そして後ろも同じです。: +以下では、`id` は属性として修正された後に、プロパティが変更されていることが確認できます。後ろも同じです: ```html run <input> @@ -164,7 +162,7 @@ HTML属性は次の特徴を持っています: </script> ``` -しかし、例外もあります。例えば、 `input.value` の同期は 属性から -> プロパティの場合のみで、逆は同期されません: +ですが、例外もあります。例えば、 `input.value` の同期は 属性から -> プロパティの場合のみで、逆は同期されません: ```html run <input> @@ -188,7 +186,7 @@ HTML属性は次の特徴を持っています: - 属性 `value` の変更は、プロパティも更新します。 - しかし、プロパティの変更は属性に影響を与えません。 -この "特徴" は実際には便利な場合があります。なぜなら、ユーザは `value` を変更する可能性があるからです。その後、HTMLから元の値を復元する場合、それは属性の中にあります。 +この "特徴" は実際には便利な場合があります。なぜなら、ユーザは `value` を変更する可能性があるからです。その後、HTMLから元の値を復元する場合、元の値は属性にあります。 ## DOM プロパティは型付けられています @@ -203,7 +201,7 @@ DOM プロパティは常に文字列とは限りません。例えば、`input. </script> ``` -他の例があります。`style` 属性は文字列ですが、`style` プロパティはオブジェクトです: +他の例です。`style` 属性は文字列ですが、`style` プロパティはオブジェクトです: ```html run <div id="div" style="color:red;font-size:120%">Hello</div> @@ -218,11 +216,11 @@ DOM プロパティは常に文字列とは限りません。例えば、`input. </script> ``` -これは重要な違いです。しかし、たとえ DOM プロパティの型が文字列でも、属性とは異なる可能性があります! +ほとんどのプロパティは文字列ですが。 -例えば `href` DOM プロパティは、たとえ属性に相対URLや単なる `#hash` が含まれていても常に *完全な* URLです。 +また、めったにありませんが、DOM プロパティの型が文字列でも、属性とは異なる場合があります。例えば `href` DOM プロパティは、属性が相対URLや単なる `#hash` だとしても、常に *完全な* URLです。 -以下、例です: +以下はその例です: ```html height=30 run <a id="a" href="#hello">link</a> @@ -235,14 +233,14 @@ DOM プロパティは常に文字列とは限りません。例えば、`input. </script> ``` -もし HTMLの中で書かれている通りの正確な `href` や他の属性値が必要な場合、`getAttrubute` を使います。 +`href` の値が必要だったり、HTMLの中で書かれている通りの正確な属性値が必要な場合は `getAttrubute` を使います。 -## 非標準の属性、データ・セット +## 非標準の属性、dataset -HTMLを記述する際には、多くの標準属性を使います。しかし、非標準、カスタムのものはどうでしょう?まず、それらが役立つのかどうか、何のために必要なのかを見てみましょう。 +HTMLの記述では、多くの標準属性を使います。しかし、非標準、カスタムのものはどうでしょう?まず、それらが役立つのかどうか、何のために必要なのかを見てみましょう。 -HTML から JavaScript へカスタムデータを渡す、もしくは JavaScript のためにHTML要素を "マークする" ために、非標準の属性が使われることがあります。 +HTML から JavaScript へカスタムデータを渡す、もしくは JavaScript のためにHTML要素を "マークする" 目的として、非標準の属性が使われることがあります。 このようになります: @@ -267,7 +265,7 @@ HTML から JavaScript へカスタムデータを渡す、もしくは JavaScri </script> ``` -また、要素のスタイルのために使われることもあります。 +また、要素のスタイルにも使われます。 例えば、ここでは注文状態に対して、属性 `order-state` が使われます: @@ -309,15 +307,15 @@ HTML から JavaScript へカスタムデータを渡す、もしくは JavaScri div.setAttribute('order-state', 'canceled'); ``` -しかし、カスタム属性には問題がある可能性があります。仮に、我々の目的のために非標準の属性を使い、後に標準がそれを発表し何かをさせるとしたらどうでしょう?HTML言語は生きており、成長し、開発者のニーズに合うようなより多くの属性が登場しています。このような場合に予期しない影響が生じることがあります。 +しかし、カスタム属性は問題にある可能性があります。非標準の属性を使っていたが、後でその属性が標準になった場合はどうなるでしょう?HTML言語は生きており、成長し、開発者のニーズに合うようなより多くの属性が登場しています。このような場合に予期しない影響が生じることがあります。 衝突を避けるために、[data-*](https://html.spec.whatwg.org/#embedding-custom-non-visible-data-with-the-data-*-attributes) 属性があります。 -**"data-" で始まるすべての属性はプログラマのために予約されています。それらは `dataset` プロパティで利用可能です。** +**"data-" で始まるすべての属性はプログラマのために予約されています。これらは `dataset` プロパティで利用可能です。** -例えば、もし `elem` が `"data-about"` という名前の属性を持っている場合、それは `elem.dataset.about` として利用できます。 +例えば、`elem` が `"data-about"` という名前の属性を持つ場合、`elem.dataset.about` でアクセスできます。 -このように: +例: ```html run <body data-about="Elephants"> @@ -360,12 +358,12 @@ div.setAttribute('order-state', 'canceled'); `data-*` 属性の利用は、カスタムデータを渡す有効かつ安全な方法です。 -data-属性の読み込みだけでなく、変更もできることに注意してください。そして、CSS はそれに応じて ビューを更新します。: 上の例では、最後の行 `(*)` で色を青に変更しています。 +data-属性の読み込みだけでなく、変更もできることに注目してください。そして、CSS はそれに応じて ビューを更新します。: 上の例では、最後の行 `(*)` で色を青に変更しています。 ## サマリ -- 属性 -- は HTML で書かれているものです。 -- プロパティ -- は DOM オブジェクトの中にあるものです。 +- 属性は HTML で書かれているものです。 +- プロパティは DOM オブジェクトの中にあるものです。 小さな比較: @@ -382,7 +380,7 @@ data-属性の読み込みだけでなく、変更もできることに注意し - `elem.removeAttribute(name)` -- 属性を削除します - `elem.attributes` はすべての属性の集合です -ほとんどのニーズに対して、DOMプロパティはうまく機能します。 次の例ように、正確な属性が必要なとき、DOMプロパティが適切でない場合にのみ属性を参照すべきです。: +ほとんどの状況で、DOMプロパティの方が好ましいです。正確な属性が必要なときなど、DOMプロパティが適切でない場合にのみ属性を参照すべきです。例えば: - 非標準の属性が必要な場合。ただし、`data-` で始まる場合は `dataset`を使うべきです。 - HTMLで "書かれている" 値を読みたい場合。 DOMプロパティの値は異なる可能性があります。例えば、 `href` プロパティは常に完全なURLであり、私たちは "オリジナル" の値を取得したい場合があります。 diff --git a/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/solution.md b/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/solution.md index fe9cf28b87..b011072102 100644 --- a/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/solution.md +++ b/2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/solution.md @@ -12,7 +12,7 @@ let text = '<b>text</b>'; elem1.append(document.createTextNode(text)); - elem2.textContent = text; - elem3.innerHTML = text; + elem2.innerHTML = text; + elem3.textContent = text; </script> ``` diff --git a/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md b/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md index d7ba4f0ed2..730fdea8e8 100644 --- a/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md +++ b/2-ui/1-document/07-modifying-document/5-why-aaa/solution.md @@ -6,4 +6,4 @@ この問題はブラウザツールを使ってDOMを調べると簡単に答えることができます。`<table>` の前に `"aaa"` があります。 -HHTML標準では、悪いHTMLを処理する方法を詳細に指定しています。このようなブラウザの動作は正しいです。 +HTML標準では、悪いHTMLを処理する方法を詳細に指定しています。このようなブラウザの動作は正しいです。 diff --git a/2-ui/1-document/07-modifying-document/article.md b/2-ui/1-document/07-modifying-document/article.md index 0b7f18a27d..3aebe1e860 100644 --- a/2-ui/1-document/07-modifying-document/article.md +++ b/2-ui/1-document/07-modifying-document/article.md @@ -1,18 +1,14 @@ # ドキュメントの変更 -DOMの変更は "ライブ" ページを作成するための鍵です。 +DOMの変更は "動的な" ページを作成するための鍵です。 -ここでは、新しい要素を "その場" で作成し、既存のページコンテンツを変更する方法を見ていきます。 - -まず、簡単な例を見てメソッドを説明します。 - -[cut] +ここでは、新しい要素を "その場" で作成し、既存のページのコンテンツを変更する方法を見ていきます。 ## 例: メッセージを表示 -はじめに、`alert` よりも見栄えの良いメッセージをページに追加する方法を見てみましょう。 +`alert` よりも見栄えの良いメッセージをページに追加する方法を見てみましょう。 -次のように見えるとしましょう: +次のように見えます: ```html autorun height="80" <style> @@ -32,41 +28,48 @@ DOMの変更は "ライブ" ページを作成するための鍵です。 */!* ``` -これは HTML の例でした。では JavaScript で同じ `div` を作ってみましょう(スタイルは依然として HTML の中、もしくは外部CSSにあると想定します)。 +これは HTML の例です。では JavaScript で同じ `div` を作ってみましょう(スタイルは HTML の中、もしくは外部CSSにあるとします)。 ## 要素の作成 -DOM ノードを作成するためのメソッドが2つあります。: +DOM ノードを作成するメソッドが2つあります。: `document.createElement(tag)` -: 与えられたタグの新しい要素を作成します: +: 指定されたタグの新しい *要素ノード* を作成します: ```js let div = document.createElement('div'); ``` `document.createTextNode(text)` -: 与えられたテキストの新しい *テキストノード* を作成します: +: 指定されたテキストの新しい *テキストノード* を作成します: ```js let textNode = document.createTextNode('Here I am'); ``` +多くの場合、メッセージのための `div` のように、要素ノードを作成する必要があります。 + ### メッセージの作成 -我々のケースでは、与えられたクラスとメッセージをその中にもつ `div` を作りたいです。: +メッセージの div の作成は次の3つのステップになります: ```js +// 1. <div> 要素の作成 let div = document.createElement('div'); -div.className = "alert alert-success"; + +// 2. クラスに "alert" をセット +div.className = "alert"; + +// 3. コンテンツをセットします div.innerHTML = "<strong>Hi there!</strong> You've read an important message."; ``` -これで、準備ができたDOM要素があります。 今は変数に入っていますが、まだページには挿入されていないため見えません。 +これで要素は作成されましたが、今は `div` という変数にあるだけで、ページにはまだ挿入されていないため見えません。 ## 挿入メソッド -`div` を表示するためには、`document` のどこかに挿入する必要があります。例えば、`document.body` です。 +`div` を表示するためには、`document` のどこかに挿入する必要があります。例えば、`document.body` で参照できる `<body>` 要素の中です。 そのための特別なメソッドがあります: `document.body.appendChild(div)`. @@ -94,75 +97,19 @@ div.innerHTML = "<strong>Hi there!</strong> You've read an important message."; </script> ``` -ここに、親要素(略して `parentElem` )へノードを挿入するメソッドの簡単なリストを示します: - -`parentElem.appendChild(node)` -: `parentElem` の最後の子として `node` を追加します。 - - 次の例は新しい `<li>` を `<ol>` の末尾に追加します: - - ```html run height=100 - <ol id="list"> - <li>0</li> - <li>1</li> - <li>2</li> - </ol> - - <script> - let newLi = document.createElement('li'); - newLi.innerHTML = 'Hello, world!'; - - list.appendChild(newLi); - </script> - ``` - -`parentElem.insertBefore(node, nextSibling)` -: `parentElem` の中の `nextSibling` の前に `node` を挿入します。 - - 次のコードは2つ目の `<li>` の前に新しいリストアイテムを挿入します: - - ```html run height=100 - <ol id="list"> - <li>0</li> - <li>1</li> - <li>2</li> - </ol> - <script> - let newLi = document.createElement('li'); - newLi.innerHTML = 'Hello, world!'; - - *!* - list.insertBefore(newLi, list.children[1]); - */!* - </script> - ``` - - 最初の要素として挿入するには、このようにします: - - ```js - list.insertBefore(newLi, list.firstChild); - ``` - -`parentElem.replaceChild(node, oldChild)` -: `parentElem` の子供の間の `oldChild` を `node` に置き換えます。 - -これらすべてのメソッドは挿入したノードを返します。言い換えると、`parentElem.appendChild(node)` は `node` を返します。しかし通常は返却値は使わず、単にメソッドを実行するだけです。 - -これらのメソッドは "古い学校" です: これらは古代から存在し、多くの古いスクリプトで見ることができます。残念なことに、それらでは解決するのが難しい作業がいくつかあります。 - -例えば、文字列として持っている *html* を挿入する方法は?もしくは指定されたノードに対し、その *前* に別のノードを挿入する方法は? もちろん、すべて実行可能ですが、エレガントな方法ではありません。 +ここでは、`document.body` に対して `append` を呼び出しましたが、他の要素の場合でも `append` でその中に要素を置くことができます。例えば `div.append(anotherElement)` で、`<div>` になにかを挿入できます。 -したがって、簡単にすべてのケースを処理する2つの挿入メソッドセットが存在します。 +その他の挿入方法は次のとおりです。挿入する場所を指定します。 -### prepend/append/before/after +- `node.append(...nodes or strings)` -- `node` の末尾にノードまたは文字列を追加します。 +- `node.prepend(...nodes or strings)` -- `node` の先頭にノードまたは文字列を挿入します。 +- `node.before(...nodes or strings)` –- `node` の前にノードまたは文字列を追加します。 +- `node.after(...nodes or strings)` –- `node` の後にノードまたは文字列を追加します。 +- `node.replaceWith(...nodes or strings)` –- `node` を与えられたノードまたは文字列で置き換えます。 -このメソッドのセットはより柔軟な挿入を提供します: +これらのメソッドの引数は挿入する任意の DOM ノードのリスト、あるいはテキスト文字列(自動的にテキストノードになります)です。 -- `node.append(...nodes or strings)` -- `node` の末尾にノードまたは文字列を追加します, -- `node.prepend(...nodes or strings)` -- `node` の先頭にノードまたは文字列を挿入します, -- `node.before(...nodes or strings)` –- `node` の前にノードまたは文字列を追加します, -- `node.after(...nodes or strings)` –- `node` の後にノードまたは文字列を追加します, -- `node.replaceWith(...nodes or strings)` –- `node` を与えられたノードまたは文字列で置き換えます。 +動きを見てみましょう。 これらのメソッドを使用して、リストに項目を追加し、項目の前後にテキストを追加する例を次に示します。: @@ -174,22 +121,22 @@ div.innerHTML = "<strong>Hi there!</strong> You've read an important message."; </ol> <script> - ol.before('before'); - ol.after('after'); + ol.before('before'); // <ol> の前に文字列 "before" を挿入します + ol.after('after'); // <ol> の後に文字列 "after" を挿入します - let prepend = document.createElement('li'); - prepend.innerHTML = 'prepend'; - ol.prepend(prepend); + let liFirst = document.createElement('li'); + liFirst.innerHTML = 'prepend'; + ol.prepend(liFirst); // <ol> の先頭に liFirst を挿入します - let append = document.createElement('li'); - append.innerHTML = 'append'; - ol.append(append); + let liLast = document.createElement('li'); + liLast.innerHTML = 'append'; + ol.append(liLast); // <ol> の末に文字列 liLast を挿入します </script> ``` これはメソッドがしていることを示す図です: -![](before-prepend-append-after.png) +![](before-prepend-append-after.svg) なので、最終的には次のようなリストになります: @@ -228,7 +175,7 @@ after <div id="div"></div> ``` -言い換えると、`elem.textContent` がするように、文字列は安全な方法で挿入されています。 +つまり、`elem.textContent` が行うように、文字列は安全な方法で挿入されます。 従って、これらのメソッドは DOM ノードまたはテキストを挿入するためにのみ使うことができます。 @@ -236,14 +183,14 @@ after ### insertAdjacentHTML/Text/Element -もう1つ、非常に多彩なメソッドがあります: `elem.insertAdjacentHTML(where、html)`。 +そのための、別の非常に用途の広いメソッドが使用できます: `elem.insertAdjacentHTML(where, html)`。 -最初のパラメータは文字列で、挿入場所を指定し、次のどれかでなければなりません: +最初のパラメータは文字列で、`elem` を基準にした挿入場所を指定します。次のいずれかでなければなりません: -- `"beforebegin"` -- `elem` の前に `html` を挿入します, +- `"beforebegin"` -- `elem` の直前に `html` を挿入します, - `"afterbegin"` -- `elem` の中の先頭に `html` を挿入します, - `"beforeend"` -- `elem` の中の末尾に `html` を挿入します, -- `"afterend"` -- `elem` の後に `html` を挿入します. +- `"afterend"` -- `elem` の直後に `html` を挿入します. 2つ目のパラメータは "そのまま" 挿入されるHTML文字列です。 @@ -269,9 +216,9 @@ after これは挿入バリエーションの図です: -![](insert-adjacent.png) +![](insert-adjacent.svg) -これと前の図との類似点は簡単に気づけます。 挿入ポイントは実際には同じですが、このメソッドはHTMLを挿入します。 +これと前の図との類似点は簡単に気づけます。 挿入箇所は実際には同じですが、このメソッドはHTMLを挿入します。 メソッドは2つの兄弟がいます: @@ -300,13 +247,57 @@ after </script> ``` -## ノードのクローン:cloneNode +## ノードの削除 + +ノードを削除するための、`node.remove()` メソッドがあります。 + +1秒後にメッセージを消してみましょう: + +```html run untrusted +<style> +.alert { + padding: 15px; + border: 1px solid #d6e9c6; + border-radius: 4px; + color: #3c763d; + background-color: #dff0d8; +} +</style> + +<script> + let div = document.createElement('div'); + div.className = "alert"; + div.innerHTML = "<strong>Hi there!</strong> You've read an important message."; + + document.body.append(div); +*!* + setTimeout(() => div.remove(), 1000); +*/!* +</script> +``` + +注意: 要素を別の場所に *移動* させたい場合は、前の場所から削除する必要はありません。 + +**すべての挿入メソッドは自動的に前の場所からノードを削除します。** + +例えば、要素を入れ替えてみましょう: + +```html run height=50 +<div id="first">First</div> +<div id="second">Second</div> +<script> + // 削除呼び出しは不要です + second.after(first); // #second を取り、その後に #first を挿入します +</script> +``` + +## ノードのクローン: cloneNode 似たようなメッセージを複数挿入するにはどうすればよいでしょう? -我々は関数を実行してコードをそこに置くことができました。 しかし、別の方法は、既存の `div` を *クローン* し、その中のテキストを変更することです(必要な場合)。 +関数を作成してコードを書くことができました。 しかし、別の方法は、既存の `div` を *クローン* し、その中のテキストを変更することです(必要であれば)。 -大きな要素を持っているケースで、この方法がより速くシンプルになる場合があります。 +大きな要素があるケースで、この方法がより速くシンプルになる場合があります。 - 呼び出し `elem.cloneNode(true)` は、すべての属性とサブ要素を含む要素の "ディープ" クローンを作成します。もし `elem.cloneNode(false)` を呼び出すと、子要素なしのクローンが作られます。 @@ -337,65 +328,154 @@ after </script> ``` -## 削除メソッド - -ノードを削除するために、次のメソッドがあります: +## DocumentFragment [#document-fragment] +`DocumentFragment` はノードのリストを渡すためのラッパーとして機能する特別な DOM ノードです。 -`parentElem.removeChild(node)` -: `parentElem` から `node` を削除します(`parentElem` の子と想定) +`DocumentFragment` へ他のノードを追加することができます。これを挿入する際は代わりにそのコンテンツが挿入されます。 -`node.remove()` -: その場所から `node` を削除します。 +例えば、以下の `getListContent` は `<li>` のアイテムのフラグメントを生成し、後で `<ul>` に挿入されます。 -2つ目のメソッドの方がはるかに短いのが分かると思います。1つ目のメソッドは歴史的な理由で存在しています。 +```html run +<ul id="ul"></ul> -````smart -もしある要素を別の場所に *移動* したいとき -- 古い場所からそれを除去する必要はありません。 +<script> +function getListContent() { + let fragment = new DocumentFragment(); -**すべての挿入メソッドは自動的に古い場所からノードを削除します** + for(let i=1; i<=3; i++) { + let li = document.createElement('li'); + li.append(i); + fragment.append(li); + } -例えば、要素を入れ替えてみましょう: + return fragment; +} -```html run height=50 -<div id="first">First</div> -<div id="second">Second</div> -<script> - // 削除呼び出しする必要はありません - second.after(first); // #second の後に #first を挿入します +*!* +ul.append(getListContent()); // (*) +*/!* </script> ``` -```` -1秒後に消えるメッセージを作りましょう: +最後の行 `(*)` では、`DocumentFragmet` を追加していますが、そのコンテンツが挿入されるので、結果の構造は次のようになります: -```html run untrusted -<style> -.alert { - padding: 15px; - border: 1px solid #d6e9c6; - border-radius: 4px; - color: #3c763d; - background-color: #dff0d8; -} -</style> +```html +<ul> + <li>1</li> + <li>2</li> + <li>3</li> +</ul> +``` + +`DocumentFragment` は明示的にはめったに使用されません。代わりにノードの配列を返すことができるからです。書き直した例です: + +```html run +<ul id="ul"></ul> <script> - let div = document.createElement('div'); - div.className = "alert alert-success"; - div.innerHTML = "<strong>Hi there!</strong> You've read an important message."; +function getListContent() { + let result = []; + + for(let i=1; i<=3; i++) { + let li = document.createElement('li'); + li.append(i); + result.push(li); + } + + return result; +} - document.body.append(div); *!* - setTimeout(() => div.remove(), 1000); - // もしくは setTimeout(() => document.body.removeChild(div), 1000); +ul.append(...getListContent()); // append + "..." operator = friends! */!* </script> ``` +`DocumentFragment` について言及するのは、主に、[template](info:template-element)要素のように、その上にいくつかの概念があるためです。これについては、後で説明します。 + +## 古典的な挿入/削除メソッド + +[old] + +歴史的な理由から、"古典的な" DOM 操作メソッドもあります。 + +これらのメソッドは本当に過去からありました。最近では `append`, `prepend`, `before`, `after`, `remove`, `replaceWith` のようなモダンなメソッドがあり、こちらのほうがより柔軟なので、古いメソッドを使う理由はありません。 + +ここにこれらのメソッドをリストしているのは、古いスクリプトで目にする可能性があるためです: + +`parentElem.appendChild(node)` +: `parentElem` の末尾の子供として `node` を追加します。 + + 次の例は、新しい `<li>` を `<ol>` の末尾に追加します。: + + ```html run height=100 + <ol id="list"> + <li>0</li> + <li>1</li> + <li>2</li> + </ol> + + <script> + let newLi = document.createElement('li'); + newLi.innerHTML = 'Hello, world!'; + + list.appendChild(newLi); + </script> + ``` + +`parentElem.insertBefore(node, nextSibling)` +: `parentElem` の中で `nextSibling` の前に `node` を挿入します。 + + 次のコードは、2つ目の `<li>` の前に新しいリスト項目を挿入します: + + ```html run height=100 + <ol id="list"> + <li>0</li> + <li>1</li> + <li>2</li> + </ol> + <script> + let newLi = document.createElement('li'); + newLi.innerHTML = 'Hello, world!'; + + *!* + list.insertBefore(newLi, list.children[1]); + */!* + </script> + ``` + 最初の要素として `newLi` を挿入するには、次のようにします: + + ```js + list.insertBefore(newLi, list.firstChild); + ``` + +`parentElem.replaceChild(node, oldChild)` +: `parentElem` の子の中で `oldChild` を `node` に置き換えます。 + +`parentElem.removeChild(node)` +: `parentElem` から `node` を削除します(`node` がその子であると想定)。 + + 次の例は `<ol>` から最初の `<li>` を削除します。: + + ```html run height=100 + <ol id="list"> + <li>0</li> + <li>1</li> + <li>2</li> + </ol> + + <script> + let li = list.firstElementChild; + list.removeChild(li); + </script> + ``` + +これらのすべてのメソッドは挿入/削除されたノードを返します。つまり、`parentElem.appendChild(node)`は `node` を返します。ですが、通常返却値は使用されず、メソッドを実行するのみです。 + ## "document.write" について -もう1つ、webページに何かを追加する非常に古代の方法があります: `document.write` です。 +もう1つ、webページに何かを追加する非常に古い方法があります: `document.write` です。 構文: @@ -409,15 +489,15 @@ after <p>The end</p> ``` -`document.write(html)` の呼び出しは `html` を "すぐにその場で" ページに書き込みます。`html` 文字列は動的に生成することができるので、柔軟性があります。JavaScriptを使用して本格的なWebページを作成し、それを書き出すことができます。 +`document.write(html)` の呼び出しは `html` を "すぐにその場で" ページに書き込みます。`html` 文字列を動的に生成することができるので柔軟性があります。JavaScriptで本格的なWebページを作成し、書き出すことができます。 -このメソッドは DOMがなく、標準もないときに出来ました... 本当に昔です。それは、それを使っているスクリプトがあるので、まだ生きています。 +このメソッドは DOMがなく、標準もないときに出来ました... 本当に昔です。まだ使用しているスクリプトがあるので生きています。 -現代のスクリプトでは、重要な制限があるためほとんど見かけません。 +重要な制約があるため、最近のスクリプトで見かけることはほとんどありません。 -**`document.write`の呼び出しはページがロードされている間だけ動作します** +**`document.write`の呼び出しはページがロードされている間だけ動作します。** -もしその後に呼び出した場合、既存のドキュメントのコンテンツが削除されます。 +もしその後に呼び出されると、既存のドキュメントのコンテンツが削除されます。 例: @@ -434,33 +514,22 @@ after したがって、上で取り上げたような他の DOMメソッドとは異なり、"ロード後" の段階では利用できません。 -それは欠点でした。 +それは欠点です。 -技術的には、ブラウザがまだ HTML を読んでいる間に `document.write` が呼び出されると、それは何かを追加し、ブラウザは最初と同じようにそれを消費します。 +利点もあります。技術的には、ブラウザがまだ HTML を読んでいる(パースしている)間に `document.write` が呼び出され、何かを追加すると、ブラウザは HTML テキストに最初からそこにあったかのように処理します。 -それは我々に良い面をもたらします -- それは *DOMの修正がないため* 驚くほど速く動作します。DOMがまだビルドされていない間、それは直接ページテキストに書き込み、ブラウザは生成時にそれをDOMを挿入します。 +これは *DOMの修正がないため* 驚くほど速く動作します。DOMがまだビルドされていない間、それは直接ページテキストに書き込み、ブラウザは生成時にそれをDOMに挿入します。 -なので、HTMLの動的に多くのテキストを追加する必要があり、またページをロードするフェーズであること、速度を考慮する必要がある場合にはそれは役立ちます。しかし実際にはこれらの要件が一緒に来ることは殆どありません。通常、このメソッドを見るときは、単に古いスクリプトだからと言う理由です。 +なので、HTMLに多くのテキストを動的に追加する必要があり、またページをロードするフェーズであること、速度を考慮する必要がある場合には役立ちます。ですが、実際にはこれらの要件が一緒に来ることは殆どありません。通常、このメソッドを見るときは、単に古いスクリプトだからと言う理由です。 ## サマリ -新しいノードを生成するメソッド: - -- `document.createElement(tag)` -- 与えられたタグを要素を作成します -- `document.createTextNode(value)` -- テキストノードを作成します (めったに使われません)), -- `elem.cloneNode(deep)` -- 要素をクローンします。 `deep==true` の場合、すべての子孫も含みます。 +- 新しいノードを生成するメソッド: + - `document.createElement(tag)` -- 指定されたタグの要素を作成します + - `document.createTextNode(value)` -- テキストノードを作成します(めったに使われません), + - `elem.cloneNode(deep)` -- 要素をクローンします。 `deep==true` の場合、すべての子孫も含みます。 -ノードの挿入と削除: - -- 親から: - - `parent.appendChild(node)` - - `parent.insertBefore(node, nextSibling)` - - `parent.removeChild(node)` - - `parent.replaceChild(newElem, node)` - - これらのメソッドはすべて `node` を返します。 - -- 与えられたノードと文字列のリスト: +- 挿入と削除: - `node.append(...nodes or strings)` -- `node` の末尾に追加します, - `node.prepend(...nodes or strings)` -- `node` の先頭に挿入します, - `node.before(...nodes or strings)` –- `node` の前に追加します, @@ -470,7 +539,15 @@ after テキスト文字列は "テキストとして" 挿入されます。 -- 与えられた HTML の一部: `elem.insertAdjacentHTML(where, html)`, `where` に応じて挿入します: +- "古典的な" メソッドもあります: + - `parent.appendChild(node)` + - `parent.insertBefore(node, nextSibling)` + - `parent.removeChild(node)` + - `parent.replaceChild(newElem, node)` + + これらのメソッドはすべて `node` を返します。 + +- 指定された HTML の一部: `elem.insertAdjacentHTML(where, html)`, `where` に応じて挿入します: - `"beforebegin"` -- `elem` の前に `html` を挿入します, - `"afterbegin"` -- `elem` の中の先頭に `html` を挿入します, - `"beforeend"` -- `elem` の中の末尾に `html` を挿入します, @@ -478,7 +555,7 @@ after また、類似メソッド `elem.insertAdjacentText` と `elem.insertAdjacentElement` があり、それらはテキスト文字列と要素を挿入しますが、めったに使われません。 -- ロードが完了する前に、ページにHTMLをつかするには: +- 読み込みが完了する前にページにHTMLを追加するには: - `document.write(html)` - ページがロードされた後、この呼び出しはドキュメントを削除します。ほぼ古いスクリプトで見られます。 + ページが読み込まれた後、この呼び出しはドキュメントを削除します。ほぼ古いスクリプトで見られます。 diff --git a/2-ui/1-document/07-modifying-document/before-prepend-append-after.png b/2-ui/1-document/07-modifying-document/before-prepend-append-after.png deleted file mode 100644 index 5bff84d850..0000000000 Binary files a/2-ui/1-document/07-modifying-document/before-prepend-append-after.png and /dev/null differ diff --git a/2-ui/1-document/07-modifying-document/before-prepend-append-after.svg b/2-ui/1-document/07-modifying-document/before-prepend-append-after.svg new file mode 100644 index 0000000000..0843713cec --- /dev/null +++ b/2-ui/1-document/07-modifying-document/before-prepend-append-after.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="409" height="203" viewBox="0 0 409 203"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="before-prepend-append-after.svg"><g id="<ol>-<li>0</li>-<li>" fill-rule="nonzero" transform="translate(18.63 60.2)"><path id="<" fill="#7E7C7B" d="M0 5.53v-.406l6.594-4.018.532.868-5.558 3.29L7.14 8.498l-.532.84z"/><path id="ol" fill="#A7333A" d="M8.568 6.3c0-1.13.294-2.023.882-2.681.588-.658 1.428-.987 2.52-.987.588 0 1.094.096 1.519.287.425.191.777.45 1.057.777.28.327.488.714.623 1.162.135.448.203.929.203 1.442 0 .56-.075 1.066-.224 1.519-.15.453-.369.838-.658 1.155-.29.317-.646.562-1.071.735a3.82 3.82 0 01-1.449.259c-.579 0-1.083-.096-1.512-.287a2.967 2.967 0 01-1.064-.777 3.168 3.168 0 01-.623-1.162A4.963 4.963 0 018.568 6.3zm1.162 0c0 .327.04.653.119.98.08.327.208.62.385.882.177.261.408.471.693.63.285.159.632.238 1.043.238.747 0 1.309-.231 1.687-.693.378-.462.567-1.141.567-2.037 0-.336-.04-.665-.119-.987a2.608 2.608 0 00-.392-.875 2.066 2.066 0 00-.7-.63c-.285-.159-.632-.238-1.043-.238-.747 0-1.307.229-1.68.686-.373.457-.56 1.139-.56 2.044zM17.836 0h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197V.938h-1.232V0z"/><path id="><" fill="#7E7C7B" d="M25.732 9.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406l-6.608 3.808zM16.8 25.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L16.8 25.53z"/><path id="li" fill="#A7333A" d="M26.236 20h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V20zm8.204 9.8v-.938h2.436v-5.124H34.44V22.8h3.556v6.062h2.38v.938H34.44zm2.058-8.988c0-.252.084-.469.252-.651a.838.838 0 01.644-.273c.27 0 .497.091.679.273a.889.889 0 01.273.651.806.806 0 01-.273.616.963.963 0 01-.679.252.872.872 0 01-.644-.252.838.838 0 01-.252-.616z"/><path id=">" fill="#7E7C7B" d="M42.532 29.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406z"/><path id="0" fill="#181717" d="M50.54 24.9c0-.793.07-1.505.21-2.135.14-.63.35-1.162.63-1.596.28-.434.635-.765 1.064-.994.43-.229.938-.343 1.526-.343.625 0 1.155.112 1.589.336.434.224.786.55 1.057.98.27.43.467.959.588 1.589.121.63.182 1.351.182 2.163a9.88 9.88 0 01-.21 2.135c-.14.63-.35 1.162-.63 1.596-.28.434-.635.765-1.064.994-.43.229-.938.343-1.526.343-.616 0-1.141-.124-1.575-.371a2.956 2.956 0 01-1.064-1.043c-.275-.448-.474-.982-.595-1.603a10.713 10.713 0 01-.182-2.051zm5.698 0c0-.495-.028-.966-.084-1.414l-4.158 3.794c.159.532.397.957.714 1.274.317.317.733.476 1.246.476.821 0 1.407-.34 1.757-1.022.35-.681.525-1.717.525-3.108zm-4.536 0c0 .233.007.457.021.672.014.215.03.425.049.63l4.172-3.78c-.159-.504-.394-.905-.707-1.204-.313-.299-.735-.448-1.267-.448-.83 0-1.416.343-1.757 1.029-.34.686-.511 1.72-.511 3.101z"/><path id="</" fill="#7E7C7B" d="M58.8 25.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L58.8 25.53zm14.084-5.698l.882.392-5.11 11.536-.882-.392 5.11-11.536z"/><path id="li" fill="#A7333A" d="M76.636 20h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V20zm8.204 9.8v-.938h2.436v-5.124H84.84V22.8h3.556v6.062h2.38v.938H84.84zm2.058-8.988c0-.252.084-.469.252-.651a.838.838 0 01.644-.273c.27 0 .497.091.679.273a.889.889 0 01.273.651.806.806 0 01-.273.616.963.963 0 01-.679.252.872.872 0 01-.644-.252.838.838 0 01-.252-.616z"/><path id="><" fill="#7E7C7B" d="M92.932 29.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406l-6.608 3.808zM16.8 45.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L16.8 45.53z"/><path id="li" fill="#A7333A" d="M26.236 40h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V40zm8.204 9.8v-.938h2.436v-5.124H34.44V42.8h3.556v6.062h2.38v.938H34.44zm2.058-8.988c0-.252.084-.469.252-.651a.838.838 0 01.644-.273c.27 0 .497.091.679.273a.889.889 0 01.273.651.806.806 0 01-.273.616.963.963 0 01-.679.252.872.872 0 01-.644-.252.838.838 0 01-.252-.616z"/><path id=">" fill="#7E7C7B" d="M42.532 49.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406z"/><path id="1" fill="#181717" d="M51.548 48.792h2.198v-7.364l-2.24 1.568-.546-.798L54.04 40h.784v8.792h2.156V49.8h-5.432z"/><path id="</" fill="#7E7C7B" d="M58.8 45.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L58.8 45.53zm14.084-5.698l.882.392-5.11 11.536-.882-.392 5.11-11.536z"/><path id="li" fill="#A7333A" d="M76.636 40h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V40zm8.204 9.8v-.938h2.436v-5.124H84.84V42.8h3.556v6.062h2.38v.938H84.84zm2.058-8.988c0-.252.084-.469.252-.651a.838.838 0 01.644-.273c.27 0 .497.091.679.273a.889.889 0 01.273.651.806.806 0 01-.273.616.963.963 0 01-.679.252.872.872 0 01-.644-.252.838.838 0 01-.252-.616z"/><path id="><" fill="#7E7C7B" d="M92.932 49.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406l-6.608 3.808zM16.8 65.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L16.8 65.53z"/><path id="li" fill="#A7333A" d="M26.236 60h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V60zm8.204 9.8v-.938h2.436v-5.124H34.44V62.8h3.556v6.062h2.38v.938H34.44zm2.058-8.988c0-.252.084-.469.252-.651a.838.838 0 01.644-.273c.27 0 .497.091.679.273a.889.889 0 01.273.651.806.806 0 01-.273.616.963.963 0 01-.679.252.872.872 0 01-.644-.252.838.838 0 01-.252-.616z"/><path id=">" fill="#7E7C7B" d="M42.532 69.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406z"/><path id="2" fill="#181717" d="M56.77 62.478c0 .859-.406 1.815-1.218 2.87-.812 1.055-1.946 2.203-3.402 3.444h4.928V69.8h-6.216v-1.008c.177-.168.42-.387.728-.658.308-.27.64-.572.994-.903a29.01 29.01 0 001.085-1.071c.369-.383.702-.775 1.001-1.176.299-.401.541-.803.728-1.204a2.75 2.75 0 00.28-1.162c0-.56-.147-1.001-.441-1.323-.294-.322-.735-.483-1.323-.483-.504 0-.929.056-1.274.168a3.101 3.101 0 00-.938.49l-.476-.77c.42-.299.866-.518 1.337-.658s.987-.21 1.547-.21c.877 0 1.54.238 1.988.714.448.476.672 1.12.672 1.932z"/><path id="</" fill="#7E7C7B" d="M58.8 65.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L58.8 65.53zm14.084-5.698l.882.392-5.11 11.536-.882-.392 5.11-11.536z"/><path id="li" fill="#A7333A" d="M76.636 60h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V60zm8.204 9.8v-.938h2.436v-5.124H84.84V62.8h3.556v6.062h2.38v.938H84.84zm2.058-8.988c0-.252.084-.469.252-.651a.838.838 0 01.644-.273c.27 0 .497.091.679.273a.889.889 0 01.273.651.806.806 0 01-.273.616.963.963 0 01-.679.252.872.872 0 01-.644-.252.838.838 0 01-.252-.616z"/><path id="></" fill="#7E7C7B" d="M92.932 69.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406l-6.608 3.808zM0 85.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L0 85.53zm14.084-5.698l.882.392-5.11 11.536-.882-.392 5.11-11.536z"/><path id="ol" fill="#A7333A" d="M16.968 86.3c0-1.13.294-2.023.882-2.681.588-.658 1.428-.987 2.52-.987.588 0 1.094.096 1.519.287.425.191.777.45 1.057.777.28.327.488.714.623 1.162.135.448.203.929.203 1.442 0 .56-.075 1.066-.224 1.519-.15.453-.369.838-.658 1.155-.29.317-.646.562-1.071.735a3.82 3.82 0 01-1.449.259c-.579 0-1.083-.096-1.512-.287a2.967 2.967 0 01-1.064-.777 3.168 3.168 0 01-.623-1.162 4.963 4.963 0 01-.203-1.442zm1.162 0c0 .327.04.653.119.98.08.327.208.62.385.882.177.261.408.471.693.63.285.159.632.238 1.043.238.747 0 1.309-.231 1.687-.693.378-.462.567-1.141.567-2.037 0-.336-.04-.665-.119-.987a2.608 2.608 0 00-.392-.875 2.066 2.066 0 00-.7-.63c-.285-.159-.632-.238-1.043-.238-.747 0-1.307.229-1.68.686-.373.457-.56 1.139-.56 2.044zm8.106-6.3h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V80z"/><path id=">" fill="#7E7C7B" d="M34.132 89.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406z"/></g><g id="Line-Copy-5-+-Line-Copy-4-+-Line-Copy-3" stroke="#C06334" stroke-linecap="square" stroke-width="3" transform="rotate(90 37 184.5)"><path id="Line-Copy-5" d="M.527.625S30.885-3.5 30.885 81.25s-15.179 94.723-15.179 94.723"/><path id="Line" d="M15 178v-17"/><path id="Line-Copy" d="M15 178l15-5"/></g><g id="Line-Copy-5-+-Line-Copy-4-+-Line-Copy-3-Copy-Copy" stroke="#C06334" stroke-linecap="square" stroke-width="3" transform="rotate(90 53.5 171.5)"><path id="Line-Copy-5" d="M.257.737s21.205 7.589 21.205 93.119v67.243"/><path id="Line" d="M21.409 162.661L14.5 150.5"/><path id="Line-Copy" d="M21.476 162.491L27.5 150.5"/></g><g id="Line-Copy-5-+-Line-Copy-4-+-Line-Copy-3-Copy-Copy-Copy" stroke="#C06334" stroke-linecap="square" stroke-width="3" transform="matrix(0 -1 -1 0 225 96)"><path id="Line-Copy-5" d="M.257.737s21.205 7.589 21.205 93.119v67.243"/><path id="Line" d="M21.409 162.661L14.5 150.5"/><path id="Line-Copy" d="M21.476 162.491L27.5 150.5"/></g><path id="Line-Copy-3" stroke="#C06334" stroke-linecap="square" stroke-width="3" d="M45.5 49.5l5-14"/><path id="Line-Copy-2" stroke="#C06334" stroke-linecap="square" stroke-width="3" d="M45.5 49.5h14"/><text id="ol.after" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="113" y="193">ol.after</tspan></text><text id="ol.append" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="113" y="156">ol.append</tspan></text><text id="ol.prepend" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="113" y="66">ol.prepend</tspan></text><g id="Line-Copy-5-+-Line-Copy-4-+-Line-Copy-3-Copy" stroke="#C06334" stroke-linecap="square" stroke-width="3" transform="matrix(0 -1 -1 0 222.5 64.5)"><path id="Line-Copy-5" d="M.527.625S30.885-3.5 30.885 81.25s-15.179 94.723-15.179 94.723"/></g><text id="ol.before" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="111" y="26">ol.before</tspan></text><text id="(…nodes-or-strings)" fill="#643B0C" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="198" y="112">(…nodes or strings)</tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/1-document/07-modifying-document/before-prepend-append-after@2x.png b/2-ui/1-document/07-modifying-document/before-prepend-append-after@2x.png deleted file mode 100644 index 44c369ee66..0000000000 Binary files a/2-ui/1-document/07-modifying-document/before-prepend-append-after@2x.png and /dev/null differ diff --git a/2-ui/1-document/07-modifying-document/insert-adjacent.png b/2-ui/1-document/07-modifying-document/insert-adjacent.png deleted file mode 100644 index 08063bc517..0000000000 Binary files a/2-ui/1-document/07-modifying-document/insert-adjacent.png and /dev/null differ diff --git a/2-ui/1-document/07-modifying-document/insert-adjacent.svg b/2-ui/1-document/07-modifying-document/insert-adjacent.svg new file mode 100644 index 0000000000..e26fd023a1 --- /dev/null +++ b/2-ui/1-document/07-modifying-document/insert-adjacent.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="409" height="203" viewBox="0 0 409 203"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="insert-adjacent.svg"><g id="<ol>-<li>0</li>-<li>" fill-rule="nonzero" transform="translate(18.63 60.2)"><path id="<" fill="#7E7C7B" d="M0 5.53v-.406l6.594-4.018.532.868-5.558 3.29L7.14 8.498l-.532.84z"/><path id="ol" fill="#A7333A" d="M8.568 6.3c0-1.13.294-2.023.882-2.681.588-.658 1.428-.987 2.52-.987.588 0 1.094.096 1.519.287.425.191.777.45 1.057.777.28.327.488.714.623 1.162.135.448.203.929.203 1.442 0 .56-.075 1.066-.224 1.519-.15.453-.369.838-.658 1.155-.29.317-.646.562-1.071.735a3.82 3.82 0 01-1.449.259c-.579 0-1.083-.096-1.512-.287a2.967 2.967 0 01-1.064-.777 3.168 3.168 0 01-.623-1.162A4.963 4.963 0 018.568 6.3zm1.162 0c0 .327.04.653.119.98.08.327.208.62.385.882.177.261.408.471.693.63.285.159.632.238 1.043.238.747 0 1.309-.231 1.687-.693.378-.462.567-1.141.567-2.037 0-.336-.04-.665-.119-.987a2.608 2.608 0 00-.392-.875 2.066 2.066 0 00-.7-.63c-.285-.159-.632-.238-1.043-.238-.747 0-1.307.229-1.68.686-.373.457-.56 1.139-.56 2.044zM17.836 0h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197V.938h-1.232V0z"/><path id="><" fill="#7E7C7B" d="M25.732 9.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406l-6.608 3.808zM16.8 25.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L16.8 25.53z"/><path id="li" fill="#A7333A" d="M26.236 20h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V20zm8.204 9.8v-.938h2.436v-5.124H34.44V22.8h3.556v6.062h2.38v.938H34.44zm2.058-8.988c0-.252.084-.469.252-.651a.838.838 0 01.644-.273c.27 0 .497.091.679.273a.889.889 0 01.273.651.806.806 0 01-.273.616.963.963 0 01-.679.252.872.872 0 01-.644-.252.838.838 0 01-.252-.616z"/><path id=">" fill="#7E7C7B" d="M42.532 29.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406z"/><path id="0" fill="#181717" d="M50.54 24.9c0-.793.07-1.505.21-2.135.14-.63.35-1.162.63-1.596.28-.434.635-.765 1.064-.994.43-.229.938-.343 1.526-.343.625 0 1.155.112 1.589.336.434.224.786.55 1.057.98.27.43.467.959.588 1.589.121.63.182 1.351.182 2.163a9.88 9.88 0 01-.21 2.135c-.14.63-.35 1.162-.63 1.596-.28.434-.635.765-1.064.994-.43.229-.938.343-1.526.343-.616 0-1.141-.124-1.575-.371a2.956 2.956 0 01-1.064-1.043c-.275-.448-.474-.982-.595-1.603a10.713 10.713 0 01-.182-2.051zm5.698 0c0-.495-.028-.966-.084-1.414l-4.158 3.794c.159.532.397.957.714 1.274.317.317.733.476 1.246.476.821 0 1.407-.34 1.757-1.022.35-.681.525-1.717.525-3.108zm-4.536 0c0 .233.007.457.021.672.014.215.03.425.049.63l4.172-3.78c-.159-.504-.394-.905-.707-1.204-.313-.299-.735-.448-1.267-.448-.83 0-1.416.343-1.757 1.029-.34.686-.511 1.72-.511 3.101z"/><path id="</" fill="#7E7C7B" d="M58.8 25.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L58.8 25.53zm14.084-5.698l.882.392-5.11 11.536-.882-.392 5.11-11.536z"/><path id="li" fill="#A7333A" d="M76.636 20h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V20zm8.204 9.8v-.938h2.436v-5.124H84.84V22.8h3.556v6.062h2.38v.938H84.84zm2.058-8.988c0-.252.084-.469.252-.651a.838.838 0 01.644-.273c.27 0 .497.091.679.273a.889.889 0 01.273.651.806.806 0 01-.273.616.963.963 0 01-.679.252.872.872 0 01-.644-.252.838.838 0 01-.252-.616z"/><path id="><" fill="#7E7C7B" d="M92.932 29.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406l-6.608 3.808zM16.8 45.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L16.8 45.53z"/><path id="li" fill="#A7333A" d="M26.236 40h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V40zm8.204 9.8v-.938h2.436v-5.124H34.44V42.8h3.556v6.062h2.38v.938H34.44zm2.058-8.988c0-.252.084-.469.252-.651a.838.838 0 01.644-.273c.27 0 .497.091.679.273a.889.889 0 01.273.651.806.806 0 01-.273.616.963.963 0 01-.679.252.872.872 0 01-.644-.252.838.838 0 01-.252-.616z"/><path id=">" fill="#7E7C7B" d="M42.532 49.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406z"/><path id="1" fill="#181717" d="M51.548 48.792h2.198v-7.364l-2.24 1.568-.546-.798L54.04 40h.784v8.792h2.156V49.8h-5.432z"/><path id="</" fill="#7E7C7B" d="M58.8 45.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L58.8 45.53zm14.084-5.698l.882.392-5.11 11.536-.882-.392 5.11-11.536z"/><path id="li" fill="#A7333A" d="M76.636 40h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V40zm8.204 9.8v-.938h2.436v-5.124H84.84V42.8h3.556v6.062h2.38v.938H84.84zm2.058-8.988c0-.252.084-.469.252-.651a.838.838 0 01.644-.273c.27 0 .497.091.679.273a.889.889 0 01.273.651.806.806 0 01-.273.616.963.963 0 01-.679.252.872.872 0 01-.644-.252.838.838 0 01-.252-.616z"/><path id="><" fill="#7E7C7B" d="M92.932 49.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406l-6.608 3.808zM16.8 65.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L16.8 65.53z"/><path id="li" fill="#A7333A" d="M26.236 60h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V60zm8.204 9.8v-.938h2.436v-5.124H34.44V62.8h3.556v6.062h2.38v.938H34.44zm2.058-8.988c0-.252.084-.469.252-.651a.838.838 0 01.644-.273c.27 0 .497.091.679.273a.889.889 0 01.273.651.806.806 0 01-.273.616.963.963 0 01-.679.252.872.872 0 01-.644-.252.838.838 0 01-.252-.616z"/><path id=">" fill="#7E7C7B" d="M42.532 69.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406z"/><path id="2" fill="#181717" d="M56.77 62.478c0 .859-.406 1.815-1.218 2.87-.812 1.055-1.946 2.203-3.402 3.444h4.928V69.8h-6.216v-1.008c.177-.168.42-.387.728-.658.308-.27.64-.572.994-.903a29.01 29.01 0 001.085-1.071c.369-.383.702-.775 1.001-1.176.299-.401.541-.803.728-1.204a2.75 2.75 0 00.28-1.162c0-.56-.147-1.001-.441-1.323-.294-.322-.735-.483-1.323-.483-.504 0-.929.056-1.274.168a3.101 3.101 0 00-.938.49l-.476-.77c.42-.299.866-.518 1.337-.658s.987-.21 1.547-.21c.877 0 1.54.238 1.988.714.448.476.672 1.12.672 1.932z"/><path id="</" fill="#7E7C7B" d="M58.8 65.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L58.8 65.53zm14.084-5.698l.882.392-5.11 11.536-.882-.392 5.11-11.536z"/><path id="li" fill="#A7333A" d="M76.636 60h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V60zm8.204 9.8v-.938h2.436v-5.124H84.84V62.8h3.556v6.062h2.38v.938H84.84zm2.058-8.988c0-.252.084-.469.252-.651a.838.838 0 01.644-.273c.27 0 .497.091.679.273a.889.889 0 01.273.651.806.806 0 01-.273.616.963.963 0 01-.679.252.872.872 0 01-.644-.252.838.838 0 01-.252-.616z"/><path id="></" fill="#7E7C7B" d="M92.932 69.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406l-6.608 3.808zM0 85.53v-.406l6.594-4.018.532.868-5.558 3.29 5.572 3.234-.532.84L0 85.53zm14.084-5.698l.882.392-5.11 11.536-.882-.392 5.11-11.536z"/><path id="ol" fill="#A7333A" d="M16.968 86.3c0-1.13.294-2.023.882-2.681.588-.658 1.428-.987 2.52-.987.588 0 1.094.096 1.519.287.425.191.777.45 1.057.777.28.327.488.714.623 1.162.135.448.203.929.203 1.442 0 .56-.075 1.066-.224 1.519-.15.453-.369.838-.658 1.155-.29.317-.646.562-1.071.735a3.82 3.82 0 01-1.449.259c-.579 0-1.083-.096-1.512-.287a2.967 2.967 0 01-1.064-.777 3.168 3.168 0 01-.623-1.162 4.963 4.963 0 01-.203-1.442zm1.162 0c0 .327.04.653.119.98.08.327.208.62.385.882.177.261.408.471.693.63.285.159.632.238 1.043.238.747 0 1.309-.231 1.687-.693.378-.462.567-1.141.567-2.037 0-.336-.04-.665-.119-.987a2.608 2.608 0 00-.392-.875 2.066 2.066 0 00-.7-.63c-.285-.159-.632-.238-1.043-.238-.747 0-1.307.229-1.68.686-.373.457-.56 1.139-.56 2.044zm8.106-6.3h2.324v7.448c0 .57.096.97.287 1.204.191.233.474.35.847.35.261 0 .511-.047.749-.14.238-.093.502-.252.791-.476l.504.77c-.15.13-.313.247-.49.35a3.638 3.638 0 01-1.106.42 2.963 2.963 0 01-1.442-.077 1.579 1.579 0 01-.679-.427 1.897 1.897 0 01-.413-.777c-.093-.322-.14-.721-.14-1.197v-6.51h-1.232V80z"/><path id=">" fill="#7E7C7B" d="M34.132 89.338l-.532-.84 5.572-3.234-5.558-3.29.532-.868 6.594 4.018v.406z"/></g><text id="ol.insertAdjacentHTM" fill="#643B0C" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="156" y="112">ol.insertAdjacentHTML(*, html)</tspan></text><g id="Line-Copy-5-+-Line-Copy-4-+-Line-Copy-3" stroke="#C06334" stroke-linecap="square" stroke-width="3" transform="rotate(90 37 184.5)"><path id="Line-Copy-5" d="M.527.625S30.885-3.5 30.885 81.25s-15.179 94.723-15.179 94.723"/><path id="Line" d="M15 178v-17"/><path id="Line-Copy" d="M15 178l15-5"/></g><g id="Line-Copy-5-+-Line-Copy-4-+-Line-Copy-3-Copy-Copy" stroke="#C06334" stroke-linecap="square" stroke-width="3" transform="rotate(90 53.5 171.5)"><path id="Line-Copy-5" d="M.257.737s21.205 7.589 21.205 93.119v67.243"/><path id="Line" d="M21.409 162.661L14.5 150.5"/><path id="Line-Copy" d="M21.476 162.491L27.5 150.5"/></g><g id="Line-Copy-5-+-Line-Copy-4-+-Line-Copy-3-Copy-Copy-Copy" stroke="#C06334" stroke-linecap="square" stroke-width="3" transform="matrix(0 -1 -1 0 225 96)"><path id="Line-Copy-5" d="M.257.737s21.205 7.589 21.205 93.119v67.243"/><path id="Line" d="M21.409 162.661L14.5 150.5"/><path id="Line-Copy" d="M21.476 162.491L27.5 150.5"/></g><path id="Line-Copy-3" stroke="#C06334" stroke-linecap="square" stroke-width="3" d="M45.5 49.5l5-14"/><path id="Line-Copy-2" stroke="#C06334" stroke-linecap="square" stroke-width="3" d="M45.5 49.5h14"/><text id="afterend" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="113" y="193">afterend</tspan></text><text id="beforeend" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="113" y="156">beforeend</tspan></text><text id="afterbegin" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="113" y="66">afterbegin</tspan></text><g id="Line-Copy-5-+-Line-Copy-4-+-Line-Copy-3-Copy" stroke="#C06334" stroke-linecap="square" stroke-width="3" transform="matrix(0 -1 -1 0 222.5 64.5)"><path id="Line-Copy-5" d="M.527.625S30.885-3.5 30.885 81.25s-15.179 94.723-15.179 94.723"/></g><text id="beforebegin" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="111" y="26">beforebegin</tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/1-document/07-modifying-document/insert-adjacent@2x.png b/2-ui/1-document/07-modifying-document/insert-adjacent@2x.png deleted file mode 100644 index 60333ad1bb..0000000000 Binary files a/2-ui/1-document/07-modifying-document/insert-adjacent@2x.png and /dev/null differ diff --git a/2-ui/1-document/08-styles-and-classes/2-create-notification/task.md b/2-ui/1-document/08-styles-and-classes/2-create-notification/task.md index 3c63c324a5..36f1a732b3 100644 --- a/2-ui/1-document/08-styles-and-classes/2-create-notification/task.md +++ b/2-ui/1-document/08-styles-and-classes/2-create-notification/task.md @@ -4,7 +4,7 @@ importance: 5 # 通知を作成する -通知の関数 `showNotification(options)` を書いてください。: `<div class="notification">` と与えられたコンテンツ。通知は1.5秒後に自動的に消える必要があります。 +通知を作成する関数 `showNotification(options)` を書いてください。通知は `<div class="notification">` という要素にしてください。通知の内容などはオプションで指定できるようにしてください。通知は1.5秒後に自動的に消えるようにしてください。 オプションは次の通りです: @@ -13,7 +13,7 @@ importance: 5 showNotification({ top: 10, // ウィンドウのトップから 10px (デフォルトは 0px) right: 10, // ウィンドウの右端から 10px (デフォルトは 0px) - html: "Hello!", // 通知の HTML + html: "Hello!", // 通知として表示する HTML className: "welcome" // div への追加クラス (任意) }); ``` diff --git a/2-ui/1-document/08-styles-and-classes/article.md b/2-ui/1-document/08-styles-and-classes/article.md index b806f2fbaf..de4605c1cb 100644 --- a/2-ui/1-document/08-styles-and-classes/article.md +++ b/2-ui/1-document/08-styles-and-classes/article.md @@ -1,37 +1,35 @@ # スタイルとクラス -JavaScriptでスタイルとクラスを扱う方法を習得する前に、 -- ここには重要なルールがあります。うまくいけばそれは十分明らかですが、それでも言及する必要があります。 +JavaScriptでスタイルとクラスを扱う方法を習得する前に、ここには重要なルールがあります。十分理解していることだと思いますが、それでも言っておかなければならないことがあります。 -一般的には要素をスタイルする2つの方法があります: +一般的には要素をスタイルする方法が2つあります: 1. CSS でクラスを生成し、それを追加します: `<div class="...">` 2. `style` に直接プロパティを書きます: `<div style="...">`. -[cut] +JavaScript はクラスと `style` プロパティの変更両方が可能です。 -CSS は常に好まれる方法です -- HTML に対してだけではなく JavaScript でも同様です。 +常に `style` よりも CSS クラスを優先すべきです。後者は、クラスでは "処理しきれない" 場合にのみ使用すべきです。 -クラスが "処理することが出来ない" 場合にのみ、`style` プロパティを操作するべきです。 - -例えば、要素の座標を動的に計算し、JavaScriptからそれを設定する場合には `style` は受け入れられます。: +例えば、要素の座標を動的に計算し、JavaScript からそれを設定する場合には `style` は受け入れられます。: ```js let top = /* 複雑な計算 */; let left = /* 複雑な計算 */; + elem.style.left = left; // e.g '123px' elem.style.top = top; // e.g '456px' ``` -テキストを赤にしたり、背景アイコンを追加するような他のケースの場合 -- CSS でそれを記述した後にそのクラスを適用します。それはより柔軟でサポートしやすくなります。 +テキストを赤にしたり、背景アイコンを追加するような他のケースの場合は、CSS でスタイルを記述した後に、そのクラスを適用します。このほうが柔軟でサポートしやすくなります。 ## className と classList -クラスを変更することは、スクリプトで最も頻繁に行われる操作の1つです。 +クラスの変更は、スクリプトで最も頻繁に行われる操作の1つです。 -ずっと昔、JavaScript には制限がありました: `"class"` のような予約語はオブジェクトプロパティにはできませんでした。その制限は今は存在しませんが、その当時は `elem.class` にように `"class"` プロパティを持つことは不可能でした。 +ずっと昔、JavaScript には制限がありました: `"class"` のような予約語はオブジェクトプロパティにはできませんでした。その制限は今は存在しませんが、その当時は `elem.class` にように `"class"` プロパティは持てませんでした。 -したがって、クラスに対しては類似したプロパティ名 `"className"` が導入されました: - `elem.className` は `"class"` 属性に対応します。 +したがって、クラスに対しては類似したプロパティ名 `"className"` が導入されました: `elem.className` は `"class"` 属性に対応します。 例: @@ -47,7 +45,7 @@ elem.style.top = top; // e.g '456px' そのための別のプロパティもあります: `elem.classList` です。 -`elem.classList` はクラスを `追加/削除/切り替える` ためのメソッドを持つ特別なオブジェクトです。 +`elem.classList` はクラスを `追加/削除/トグル` するためのメソッドを持つ特別なオブジェクトです。 例: @@ -64,15 +62,15 @@ elem.style.top = top; // e.g '456px' </body> ``` -したがって、`className` を使って完全なクラス文字列操作したり、`classList` を使って個々のクラスを操作することができます。私たちが選ぶものは、私たちのニーズに応じたものになります。 +したがって、`className` で完全なクラス文字列全体を操作したり、`classList` で個々のクラスを操作することができます。ケースに応じて必要な物を使います。 `classList` のメソッド: - `elem.classList.add/remove("class")` -- クラスの追加/削除をします。 -- `elem.classList.toggle("class")` -- もしクラスが存在する場合は削除します。そうでなければ追加します。 -- `elem.classList.contains("class")` -- 指定されたクラスをチェックし、 `true/false` を返します。 +- `elem.classList.toggle("class")` -- クラスが存在する場合は削除します。なければ追加します。 +- `elem.classList.contains("class")` -- クラスをの有無をチェックし、 `true/false` を返します。 -それに加え、`classList` は反復可能です。なので、このようにすべてのクラスを列挙する事ができます: +それに加え、`classList` は反復可能です。なので、以下のように `for..of` ですべてのクラスの列挙もできます: ```html run <body class="main page"> @@ -103,23 +101,23 @@ document.body.style.backgroundColor = prompt('background color?', 'green'); ``` ````smart header="プレフィックス付きのプロパティ" -`-moz-border-radius` や `-webkit-border-radius` のようなブラウザプレフィックスが付いたプロパティもまた同じルールに従います。例えば: +`-moz-border-radius` や `-webkit-border-radius` のようなブラウザプレフィックスが付いたプロパティもまた同じルールに従います。 + +例えば: ```js button.style.MozBorderRadius = '5px'; button.style.WebkitBorderRadius = '5px'; ``` - -つまり: ダッシュ `"-"` は大文字になります。 ```` ## スタイルプロパティのリセット -時々、スタイルプロパティを割り当て、後ほどそれを削除したい場合があります。 +スタイルプロパティを割り当てた後、削除したい場合があります。 -例えば、要素を隠すために `elem.style.display = "none"` と設定できます。 +例えば、要素を隠すには `elem.style.display = "none"` とします。 -そして、後でそれがセットされていなかったかのように、 `style.display` を削除したいかもしれません。`delete elem.style.display` の代わりに空行を割り当てるべきです: `elem.style.display = ""`. +そして、後でそれがセットされていなかったかのように、`style.display` を削除したいかもしれません。この場合、`delete elem.style.display` を行う代わりに空行を割り当てるべきです: `elem.style.display = ""`. ```js run // このコードを実行すると、<body> が "点滅" します @@ -130,6 +128,14 @@ setTimeout(() => document.body.style.display = "", 1000); // 通常に戻りま `display` を空文字列に設定した場合、ブラウザは通常 CSS クラスと組み込みのスタイルを適用します。まるで `style` プロパティが全くないかのように振る舞います。 +また、このための特別なメソッドもあります, `elem.style.removeProperty('style property')`. 次のようにしてプロパティの削除ができます。: + +```js run +document.body.style.background = 'red'; //background を red にします + +setTimeout(() => document.body.style.removeProperty('background'), 1000); // 1秒後に background を削除します +``` + ````smart header="`style.cssText` で完全に書き直す" 通常、個々のスタイルプロパティを割り当てるために `style.*` を使います。`div.style` はオブジェクトであり、読み取り専用であるため、`div.style="color: red; width: 100px"` のような完全なスタイルを設定することはできません。 @@ -150,16 +156,16 @@ setTimeout(() => document.body.style.display = "", 1000); // 通常に戻りま </script> ``` -このような代入は既存のすべてのスタイルを削除するので、めったに使われません。: それは追加ではなく置換です。場合によっては必要なものを削除する可能性があります。しかし、私たちが重要なものを削除しないことを知っているとき、新しい要素に対しては行う事ができます。 +このような代入は既存のすべてのスタイルを削除するので、めったに使われません。: これは追加ではなく置換です。場合によっては必要なものを削除する可能性があります。しかし、重要なものを削除しないと分かっているとき、例えば新しい要素に対しては安全に使用できます。 -属性を設定することで同じことを達成することが出来ます。: `div.setAttribute('style', 'color: red...')`. +属性を設定することで同じことができます。: `div.setAttribute('style', 'color: red...')`. ```` -## 単位を気にする +## 単位に留意しましょう -CSS の単位はスタイルの値の中で指定する必要があります。 +値に CSS の単位を追加するのを忘れないようにましょう。 -例えば、`elem.style.top` は `10` ではなくむしろ `10px` とする必要があります。そうれなければ動作しません: +例えば、`elem.style.top` は `10` ではなく `10px` とする必要があります。そうでない場合動作しません: ```html run height=100 <body> @@ -180,17 +186,17 @@ CSS の単位はスタイルの値の中で指定する必要があります。 </body> ``` -ブラウザが最後の行で `style.margin` を "アンパック" し、`style.marginLeft`と `style.marginTop`(および他の部分的なマージン)をどのように推測するか注意してください。 +ブラウザが最後の行で `style.margin` を "アンパック" し、`style.marginLeft`と `style.marginTop` をどのように推測するか注意してください。 ## 算出スタイル: getComputedStyle -スタイルを変更するのは簡単です。しかしどうやってそれを *読み* ますか? +スタイルを変更するのは簡単です。しかしどうやってそれを *参照* しますか? 例えば、要素のサイズ、マージン、色が知りたいです。どうやりますか? **`style` プロパティは CSS カスケードなしで、その `"style"` 属性の値だけを操作します。** -なので、`elem.style` を使って CSS クラスから来たものを読むことは出来ません。 +そのため、`elem.style` では CSS クラスから来たスタイルを参照することはできません。 例えば、ここでは `style` はマージンを見ません: @@ -210,9 +216,9 @@ CSS の単位はスタイルの値の中で指定する必要があります。 </body> ``` -...しかし仮にマージンを 20px に増加する必要がある場合はどうすればよいでしょう?そのために現在の値がほしいです。 +...ですが、仮にマージンを `20px` 増やす必要がある場合はどうすればよいでしょう? そのために現在の値が必要です。 -そのための別のメソッドがあります: `getComputedStyle`. +そのためのもう1つのメソッドがあります: `getComputedStyle`. 構文は次の通りです: @@ -224,9 +230,9 @@ element : 値を読み取る要素 pseudo -: 疑似要素(必要な場合)。例えば `::before`。空文字列または引数なしは要素自身を意味します。 +: 疑似要素(必要な場合に指定)。例えば `::before`。空文字列または引数なしは要素自身を意味します。 -結果は、`elem.style` のようにスタイルプロパティを持つオブジェクトですが、今はすべての CSS クラスに関してのものです。 +結果は、`elem.style` のようにスタイルプロパティを持つオブジェクトですが、ここではすべての CSS クラスに関してのものです。 例: @@ -248,22 +254,21 @@ pseudo </body> ``` -```smart header="算出(comupted)と解析(resolved)値" +```smart header="算出(comupted)と解決(resolved)値" [CSS](https://drafts.csswg.org/cssom/#resolved-values) には2つのコンセプトがあります: 1. *算出* スタイル値(*computed* style value)は、CSS カスケードの結果として、すべての CSS ルールと CSS 継承が適用された後の値です。`height:1em` または `font-size:125%` のように見えます。 +2. *解決* スタイル値(*resolved* style value)は要素に最終的に適用される値です。`1em` or `125%` のような値は相対的なものです。ブラウザは計算された値を取り、`height: 20px` や` font-size: 16px` のようにすべての単位を固定し絶対的にします。ジオメトリ プロパティの場合、解析された値は `width:50.5px`のような浮動小数点を持ちます。 -2. *解析* スタイル値(*resolved* style value)は要素に最終的に適用される値です。`1em` or `125%` のような値は相対的なものです。ブラウザは計算された値を取り、`height: 20px` や` font-size: 16px` のようにすべての単位を固定し絶対的にします。 幾何学プロパティの場合、解析された値は `width:50.5px`のような浮動小数点を持ちます。 - -ずっと以前に、`getComputedStyle` は計算された値を取得するために作られましたが、解析された値がはるかに便利であることが分かり、標準が変更されました。 +ずっと以前に、`getComputedStyle` は算出値を取得するために作られましたが、解決値がはるかに便利であることが分かり、標準が変更されました。 -したがって、最近では `getComputedStyle` は実際にはプロパティの解析された値を返します。 +したがって、最近では `getComputedStyle` は実際にはプロパティの解決された値を返します。通常、ジオメトリ に対しては `px` です。 ``` ````warn header="`getComputedStyle` には完全なプロパティ名が必要です" -`paddingLeft` や `marginTop` や `borderTopWidth` のように、常に正確なプロパティを求めてください。 そうしないと、正しい結果が保証されません。 +`paddingLeft` や `marginTop` や `borderTopWidth` のように、常に正確なプロパティの指定が必要です。 そうしないと、正しい結果が保証されません。 -例えば、`paddingLeft/paddingTop` プロパティがある場合、`getComputedStyle(elem).padding` のためにどうすればよいでしょうか?何もない、または知られているパディングから"生成された" 値があるでしょうか?そこに標準的なルールはありません。 +例えば、`paddingLeft/paddingTop` プロパティがある場合、`getComputedStyle(elem).padding` では何が取得されるでしょうか?何もない、または知られているパディングから"生成された" 値があるでしょうか?そこに標準的なルールはありません。 他にも矛盾があります。 たとえば、一部のブラウザ(Chrome)では下記のドキュメントに"10px" と表示されていますが、他の一部のブラウザ(Firefox)では -- そうではありません: @@ -280,12 +285,12 @@ pseudo ``` ```` -```smart header="\"訪問された\" リンクのスタイルは表示されません!" +```smart header="`:visited` リンクが適用されたスタイルは表示されません!" 訪問されたリンクは `:visited` CSS 疑似クラスを使って色付けされるかもしれません。 -しかし、`getComputedStyle` はその色へのアクセスを提供しません。なぜなら、そうでなければ、任意のページが、ページ上でそれを作りスタイルをチェックすることによって、ユーザがリンクを訪れたかどうかを知ることができるためです。 +しかし、`getComputedStyle` はその色へのアクセスを提供しません。なぜなら、アクセスできると、任意のページ上でそれを作りスタイルをチェックすることによって、ユーザがリンクを訪れたかどうかを知ることができるためです。 -JavaScriptでは、 `:visited` によって適用されたスタイルは見えません。また、`:visited` にジオメトリの変更スタイルを適用することを禁止するという制限が CSS にはあります。それは、リンクが訪問された場合に悪意のあるページがテストしてプライバシを破る方法がないことを保証するためです。 +JavaScriptでは、 `:visited` によって適用されたスタイルは見えません。また、`:visited` にジオメトリの変更スタイルを適用することを禁止するという制限が CSS にはあります。それは、リンクが訪問された場合に悪意のあるページがテストしてプライバシーを破る方法がないことを保証するためです。 ``` ## サマリ @@ -295,7 +300,7 @@ JavaScriptでは、 `:visited` によって適用されたスタイルは見え - `className` -- 文字列値で、クラスのセット全体を管理するのに良いです。 - `classList` -- メソッド `add/remove/toggle/contains` を持つオブジェクトで, 個々のクラスを管理するのに良いです。 -スタイルを変更するために: +スタイルを変更するために以下の方法があります: - `style` プロパティはキャメルケース化されたスタイルを持つオブジェクトです。それへの読み書きは、 `"style"` 属性の個々のプロパティを変更するのと同じ意味を持ちます。`important` や他の珍しいものを適用する方法を知るには -- [MDN](mdn:api/CSSStyleDeclaration) にメソッドのリストがあります。 diff --git a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.png b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.png deleted file mode 100644 index 098f9eb6d5..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.svg b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.svg new file mode 100644 index 0000000000..4ae90b1c71 --- /dev/null +++ b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="233" height="156" viewBox="0 0 233 156"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="field.svg"><image id="Screen-Shot-2017-02-25-at-23.45.22" width="224" height="150" x="4" y="3" opacity=".7" xlink:href="data:image/png;base64,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"/><text id="(0,0)" fill="#A7333A" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="45" y="60">(0,0)</tspan></text><circle id="Oval" cx="15.5" cy="15.5" r="4.5" fill="#C06334"/><text id="clientWidth" fill="#A7333A" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="73" y="114">clientWidth</tspan></text><path id="Line-9" fill="#C06334" fill-rule="nonzero" d="M24.114 22.183l20.711 4.719-5.078 6.181 13.705 11.258 1.16.952-1.905 2.318-1.16-.952-13.704-11.258-5.078 6.183-8.651-19.401z"/><path id="Line-10" fill="#C06334" fill-rule="nonzero" d="M197 118l19 9.5-19 9.5v-8H34v8l-19-9.5 19-9.5v8h163v-8z"/></g></g></svg> \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field@2x.png b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field@2x.png deleted file mode 100644 index 3ecb293753..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/field@2x.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.md b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.md index d8c1fbfca2..7adab17d84 100644 --- a/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.md +++ b/2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.md @@ -2,7 +2,7 @@ 座標は、フィールド内部の左上の角から始まります: -![](field.png) +![](field.svg) 内部フィールドの幅/高さは `clientWidth/clientHeight` です。なので、フィールドの中央は、座標 `(clientWidth/2, clientHeight/2)` となります。 diff --git a/2-ui/1-document/09-size-and-scroll/article.md b/2-ui/1-document/09-size-and-scroll/article.md index 5f0a0c6047..5562b68694 100644 --- a/2-ui/1-document/09-size-and-scroll/article.md +++ b/2-ui/1-document/09-size-and-scroll/article.md @@ -30,7 +30,7 @@ JavaScript では、要素を移動したり配置するときに、座標を正 要素はこのように見えます: -![](metric-css.png) +![](metric-css.svg) [サンドボックスでドキュメントを開く](sandbox:metric) ことができます。 @@ -50,7 +50,7 @@ JavaScript では、要素を移動したり配置するときに、座標を正 ここに全体像があります: -![](metric-all.png) +![](metric-all.svg) それらは多くのプロパティがあり、1つの図でそれらすべてを表現するのは難しいです。が、それらの値は単純で理解しやすいものです。 @@ -83,7 +83,7 @@ JavaScript では、要素を移動したり配置するときに、座標を正 </script> ``` -![](metric-offset-parent.png) +![](metric-offset-parent.svg) `offsetParent` が `null` である場合がいくつかあります: @@ -97,7 +97,7 @@ JavaScript では、要素を移動したり配置するときに、座標を正 これら2つのプロパティは最もシンプルなものです。これらは要素の "外部の" 幅/高さを提供します。もしくは、言い換えると、ボーダーを含むその要素の完全なサイズです。 -![](metric-offset-width-height.png) +![](metric-offset-width-height.svg) サンプル要素の場合: @@ -109,7 +109,7 @@ JavaScript では、要素を移動したり配置するときに、座標を正 もし要素(またはその祖先)が `display:none` を持っている、もしくはドキュメント上にない場合、それに応じてすべてのジオメトリプロパティはゼロもしくは `null` になります。 -例えば、`offsetParent` が `null` で `offsetWidt`, `offsetHeight` は `0` です。 +例えば、`offsetParent` が `null` で `offsetWidth`, `offsetHeight` は `0` です。 私たちは次のように、これを要素が隠されているかをチェックするための使う事ができます: @@ -133,7 +133,7 @@ function isHidden(elem) { - `clientLeft = 25` -- 左のボーダー幅 - `clientTop = 25` -- トップのボーダー幅 -![](metric-client-left-top.png) +![](metric-client-left-top.svg) ...しかし正確には -- それらはボーダーではなく、外側から内側の相対座標です。 @@ -143,7 +143,7 @@ function isHidden(elem) { この場合、今回の例の `clientLeft` は `25` ではなく、スクロールバーの幅を含めたものになります `25+16=41`: -![](metric-client-left-top-rtl.png) +![](metric-client-left-top-rtl.svg) ## clientWidth/Height @@ -151,7 +151,7 @@ function isHidden(elem) { パディングと一緒にコンテンツの幅を含みますが、スクロールバーは含まれません。: -![](metric-client-width-height.png) +![](metric-client-width-height.svg) 上の図では、最初に `clientHeight` を考えましょう: これは評価するのが簡単です。水平スクロールバーがないので、ボーダーの内側の合計になります: CSS高さ `200px` に top と bottom のパディング(`2*20px`) を加えた合計 `240px` です。 @@ -159,7 +159,7 @@ function isHidden(elem) { **もしパディングがない場合、`clientWidth/Height` は丁度ボーダーとスクロールバー(存在する場合)の内側のコンテンツ領域なります。** -![](metric-client-width-nopadding.png) +![](metric-client-width-nopadding.svg) したがって、パディングがない場合には、コンテンツ領域のサイズを取得するために `clientWidth/clientHeight` を使うことができます。 @@ -168,7 +168,7 @@ function isHidden(elem) { - プロパティ `clientWidth/clientHeight` は要素の可視部分のみを占めます。 - プロパティ `scrollWidth/scrollHeight` はスクロールアウトされた(隠れた)部分も含めます: -![](metric-scroll-width-height.png) +![](metric-scroll-width-height.svg) 上の図では: @@ -198,7 +198,7 @@ element.style.height = element.scrollHeight + 'px'; 下の図では、縦スクロールのブロックに対する `scrollHeight` と `scrollTop` を見ることが出来ます。 -![](metric-scroll-top.png) +![](metric-scroll-top.svg) つまり、`scrollTop` は "どのくらいスクロールされているか" です。 @@ -247,7 +247,7 @@ alert( getComputedStyle(elem).width ); // 要素の CSS幅を表示します また、もう1つ理由があります: スクロールバーです。スクロールバーがない場合に上手く動作するコードは、スクロールバーがあると不具合を引き起こすことがあります。なぜなら、スクロールバーは、ブラウザによってはコンテンツからスペースを取るためです。従って、コンテンツのために本当に利用可能な幅は CSS 幅よりも *小さい* です。そして、`clientWidth/clientHeight` はそれを考慮します。 -...しかし、`getComputedStyle(elem).width` を使った状況では異なります。一部のブラウザ(e.g. Chrome) ではスクロールバーを引いた実際の内部幅を返し、別のいくつか(e.g. Firefox)は -- CSS 幅を返します(スクロールバーを無視)。このようなクロスブラザの差異が、`getComputedStyle` の利用ではなく、むしろジオメトリプロパティに頼るべき理由です。 +...しかし、`getComputedStyle(elem).width` を使った状況では異なります。一部のブラウザ(e.g. Chrome) ではスクロールバーを引いた実際の内部幅を返し、別のいくつか(e.g. Firefox)は -- CSS 幅を返します(スクロールバーを無視)。このようなクロスブラウザの差異が、`getComputedStyle` の利用ではなく、むしろジオメトリプロパティに頼るべき理由です。 ```online diff --git a/2-ui/1-document/09-size-and-scroll/metric-all.png b/2-ui/1-document/09-size-and-scroll/metric-all.png deleted file mode 100644 index 6deec90821..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-all.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-all.svg b/2-ui/1-document/09-size-and-scroll/metric-all.svg new file mode 100644 index 0000000000..a5dadb47f0 --- /dev/null +++ b/2-ui/1-document/09-size-and-scroll/metric-all.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="670" height="602" viewBox="0 0 670 602"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><linearGradient id="linearGradient-1" x1="50%" x2="50%" y1="0%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#D1CFCD"/></linearGradient><linearGradient id="linearGradient-2" x1="50%" x2="50%" y1="0%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#D1CFCD"/></linearGradient></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="metric-all.svg"><text id="Introduction" fill="#643B0C" font-family="OpenSans-Bold, Open Sans" font-size="16" font-weight="bold"><tspan x="160" y="94">Introduction</tspan> <tspan x="160" y="122" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">This Ecma Standard is based on several </tspan> <tspan x="160" y="141" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">originating technologies, the most well </tspan> <tspan x="160" y="160" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">known being JavaScript (Netscape) and </tspan> <tspan x="160" y="179" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">JScript (Microsoft). The language was </tspan> <tspan x="160" y="198" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">invented by Brendan Eich at Netscape and </tspan> <tspan x="160" y="217" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">first appeared in that company’s Navigator </tspan> <tspan x="160" y="236" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">2.0 browser. It has appeared in all </tspan> <tspan x="160" y="255" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">subsequent browsers from Netscape and </tspan> <tspan x="160" y="274" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">in all browsers from Microsoft starting with </tspan> <tspan x="160" y="293" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">Internet Explorer 3.0.</tspan> <tspan x="160" y="312" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">The development of this Standard started </tspan> <tspan x="160" y="331" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">in November 1996. The first edition of this </tspan> <tspan x="160" y="350" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">Ecma Standard was adopted by the Ecma </tspan> <tspan x="160" y="369" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">General Assembly of June 1997.</tspan> <tspan x="160" y="388" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">That Ecma Standard was submitted to ISO/</tspan> <tspan x="160" y="407" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">IEC JTC 1 for adoption under the fast-track </tspan> <tspan x="160" y="426" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">procedure, and approved as international </tspan> <tspan x="160" y="445" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">standard ISO/IEC 16262, in April 1998. The </tspan> <tspan x="160" y="464" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">Ecma General Assembly of June 1998 </tspan> <tspan x="160" y="483" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">approved the second edition of ECMA-262 </tspan> <tspan x="160" y="502" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">to keep it fully aligned with ISO/IEC 16262. </tspan> <tspan x="160" y="521" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">Changes between the first and the second </tspan> <tspan x="160" y="540" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">edition are editorial in nature.</tspan></text><path id="Rectangle-1" fill="#DBAF88" d="M491 162v290H117V162h374zm-25 25H142v240h324V187z"/><path id="Rectangle-2" stroke="#DBAF88" stroke-width="2" d="M141 62h326v500H141z"/><text id="scrollHeight" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-90 592 310)"><tspan x="541.6" y="314.5">scrollHeight</tspan></text><text id="offsetHeight" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-90 552 310)"><tspan x="501.6" y="314.5">offsetHeight</tspan></text><text id="scrollTop" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-90 618 125)"><tspan x="580.2" y="129.5">scrollTop</tspan></text><path id="Line-27" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M466.5 62H640"/><path id="Line-28" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M492.5 163h92.14"/><path id="Line-29" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M492.5 451h92.14"/><path id="Line-33" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M467.5 189H640"/><path id="Line-32" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M467.5 427h72.14"/><path id="Line-26" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M466.5 561h148.14"/><path id="Line-25" fill="#C06334" fill-rule="nonzero" d="M605 64.5l7 14h-6v466h6l-7 14-7-14h6v-466h-6l7-14z"/><path id="Line-30" fill="#C06334" fill-rule="nonzero" d="M565 164.5l7 14h-6v255h6l-7 14-7-14h6v-255h-6l7-14z"/><text id="clientHeight" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-90 510 304)"><tspan x="459.6" y="308.5">clientHeight</tspan></text><path id="Line-34" fill="#C06334" fill-rule="nonzero" d="M523 191.5l7 14h-6v206h6l-7 14-7-14h6v-206h-6l7-14z"/><text id="offsetTop" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-90 104 83)"><tspan x="66.2" y="87.5">offsetTop</tspan></text><text id="clientLeft" fill="#166388" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-90 130.5 237)"><tspan x="88.5" y="241.5">clientLeft</tspan></text><path id="Line-36" fill="#C06334" fill-rule="nonzero" d="M117 4.5l7 14h-6v128h6l-7 14-7-14h6v-128h-6l7-14z"/><path id="Line-31" fill="#C06334" fill-rule="nonzero" d="M631 64.5l7 14h-6v96.499l6 .001-7 14-7-14 6-.001V78.5h-6l7-14z"/><path id="Rectangle-14" fill="#FFF" d="M154 73h312v89H154z"/><path id="Rectangle-15" fill="#FFF" d="M154 451h312v93H154z"/><path id="Line-39" fill="#C06334" fill-rule="nonzero" d="M431 479.09l14 7-14 7-.001-6h-271.36l.001 6-14-7 14-7-.001 6h271.36l.001-6z"/><path id="Line-42" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M445.64 510v-84"/><path id="Line-43" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M141.64 510v-84"/><text id="clientWidth" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="261.3" y="478">clientWidth</tspan></text><path id="Line-41" fill="#C06334" fill-rule="nonzero" d="M100 156.09l14 7-14 7v-6H18.639l.001 6-14-7 14-7-.001 6H100v-6z"/><text id="clientTop" fill="#166388" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="147.7" y="178">clientTop</tspan></text><text id="offsetLeft" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="17.5" y="154">offsetLeft</tspan></text><path id="Line-40" fill="#C06334" fill-rule="nonzero" d="M475 522.09l14 7-14 7-.001-6h-340.36l.001 6-14-7 14-7-.001 6h340.36l.001-6z"/><path id="Line-45" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M490.64 551V447"/><path id="Line-44" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M116.64 551V447"/><text id="offsetWidth" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="258.3" y="516">offsetWidth</tspan></text><path id="Rectangle-233" stroke="#AF6E24" stroke-width="2" d="M1 1h668v600H1z"/><g id="Group" transform="translate(450 187)"><rect id="Rectangle-19" width="15" height="239" x=".5" y=".5" fill="#D1CFCD" stroke="#D1CFCD" rx="3"/><g id="Rectangle-18-+-Triangle-1"><rect id="Rectangle-18" width="15" height="19" x=".5" y=".5" fill="url(#linearGradient-1)" stroke="#D1CFCD" rx="3"/><path id="Triangle-1" fill="#7E7C7B" d="M8 7l3.2 6H4.8z"/></g><g id="Rectangle-18-+-Triangle-2" transform="matrix(1 0 0 -1 0 240)"><rect id="Rectangle-18" width="15" height="19" x=".5" y=".5" fill="url(#linearGradient-1)" stroke="#D1CFCD" rx="3"/><path id="Triangle-1" fill="#7E7C7B" d="M8 7l3.2 6H4.8z"/></g><g id="Rectangle-18-+-Triangle-3-+-Group" transform="translate(0 50)"><g id="Rectangle-18-+-Triangle-3" fill="url(#linearGradient-2)" stroke="#D1CFCD" transform="matrix(1 0 0 -1 0 51)"><rect id="Rectangle-18" width="15" height="50" x=".5" y=".5" rx="3"/></g><g id="Group" fill="#D1CFCD" stroke="#7E7C7B" transform="translate(4 20)"><path id="Rectangle-22" d="M.5.5h7v1h-7z"/><path id="Rectangle-23" d="M.5 3.5h7v1h-7z"/><path id="Rectangle-24" d="M.5 6.5h7v1h-7z"/><path id="Rectangle-25" d="M.5 9.5h7v1h-7z"/></g></g></g><g id="Group" transform="translate(115.676 162.5)"><g id="Line-4-+-Line-5" stroke="#166388" stroke-linecap="square" stroke-width="2" transform="translate(22.324 18.5)"><path id="Line-4" d="M2.5.5L0 6" transform="matrix(1 0 0 -1 0 6)"/><path id="Line-5" d="M5.5.5L3 6" transform="rotate(180 4.5 3)"/></g><g id="Line-4-+-Line-6" stroke="#166388" stroke-linecap="square" stroke-width="2" transform="matrix(1 0 0 -1 22.324 6.5)"><path id="Line-4" d="M2.5.5L0 6" transform="matrix(1 0 0 -1 0 6)"/><path id="Line-5" d="M5.5.5L3 6" transform="rotate(180 4.5 3)"/></g><path id="Line-49" fill="#166388" stroke="#166388" stroke-linecap="round" stroke-linejoin="bevel" stroke-width="2" d="M25.824 25.5h-25"/><path id="Line-50" fill="#166388" stroke="#166388" stroke-linecap="round" stroke-linejoin="bevel" stroke-width="2" d="M25.324 25V0"/><path id="Line-4" fill="#166388" fill-rule="nonzero" d="M19.328 21.676l.91.414 5.5 2.5.91.413-.589 1.296.09.198-.226.1-.102.227-.198-.09-.385.176-5.5 2.5-.91.414-.828-1.82.91-.414 4.297-1.954-3.797-1.726-.91-.413.828-1.821zM7.32 21.676l.828 1.82-.91.414-3.798 1.726 4.298 1.954.91.413-.827 1.821-.91-.414-5.5-2.5-.387-.176-.196.09-.103-.225-.225-.102.089-.198L0 25.003l.91-.413 5.5-2.5.91-.414z"/></g></g></g></svg> \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/metric-all@2x.png b/2-ui/1-document/09-size-and-scroll/metric-all@2x.png deleted file mode 100644 index e084c257b1..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-all@2x.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl.png b/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl.png deleted file mode 100644 index ffb10aa1bd..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl.svg b/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl.svg new file mode 100644 index 0000000000..dd9e17cf8b --- /dev/null +++ b/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="359" height="316" viewBox="0 0 359 316"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><linearGradient id="linearGradient-1" x1="0%" x2="62.299%" y1="47.096%" y2="47.096%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#FFF"/></linearGradient><linearGradient id="linearGradient-2" x1="50%" x2="50%" y1="0%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#D1CFCD"/></linearGradient><linearGradient id="linearGradient-3" x1="50%" x2="50%" y1="0%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#D1CFCD"/></linearGradient></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="metric-client-left-top-rtl.svg"><path fill="#FFF" d="M0 0h359v316H0z"/><path id="היא-שפת-תסריט-מפורשת" fill="#643B0C" d="M336.484 135v-10.031h4.047V135h-4.047zm13.594 0v-9.063c0-2.416-.206-3.856-.617-4.32-.412-.463-1.685-.695-3.82-.695h-12.047l1.547-4.625h11.171c3.136 0 5.23.458 6.282 1.375a3.733 3.733 0 011.234 1.984c.198.792.297 2.177.297 4.156V135h-4.047zm-20.39-6.266h-4.063c.73-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.954-.72-.479-1.964-.895-3.735-1.25l2.094-3.89c2.552.51 4.33 1.21 5.336 2.101 1.005.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM302.063 135v-.516c0-2.677.28-4.971.843-6.882.563-1.912 1.51-3.8 2.844-5.665l-.703-.671c-1.136-1.063-2.162-1.948-3.078-2.657l3.687-2.687c2.448 1.937 5.037 4.51 7.766 7.719.625-1.084 1.039-2.032 1.242-2.844.203-.813.305-1.927.305-3.344v-1.156h4.328v.906c0 3.521-1.182 6.568-3.547 9.14a41.618 41.618 0 014.422 6.47l-3.485 2.578c-2.062-3.709-4.864-7.355-8.406-10.938-1.26 1.958-1.89 4.948-1.89 8.969V135h-4.329zm-31.954 0l-2.203-17.5 3.922-1.578 1.172 9.25c1.135-.281 1.932-.945 2.39-1.992.459-1.047.688-2.716.688-5.008v-1.875h4.344v1.719c0 3.093-.565 5.445-1.695 7.054-1.13 1.61-2.92 2.607-5.368 2.993l.282 2.312c2.718 0 4.89-.453 6.515-1.36 2.844-1.583 4.266-5.192 4.266-10.827v-1.891h4.328v1.562c0 5.615-1.58 9.875-4.742 12.782-3.162 2.906-7.794 4.359-13.899 4.359zm-21.937 0l1.39-4.234h10.47v-4.157c0-1.948-.193-3.205-.579-3.773-.385-.568-1.385-1.065-3-1.492l-2.984-.781-.235.671c-.135.396-.203.74-.203 1.032 0 1.01.87 1.583 2.61 1.718l-1.078 3.25c-3.5-.323-5.25-1.671-5.25-4.046 0-.917.374-2.438 1.125-4.563l.968-2.703 7.125 1.734c2.313.563 3.815 1.284 4.508 2.164.693.88 1.04 2.503 1.04 4.868v6.078L262.671 135h-14.5zm-22.969 0l1.547-4.625h4.031c-.823-2.115-1.234-4.708-1.234-7.781v-1.672h-2.328l1.547-4.625h8.578c3.135 0 5.229.458 6.281 1.375a3.733 3.733 0 011.234 1.984c.198.792.297 2.177.297 4.156V135h-4.047v-9.063c0-2.416-.203-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-3.063v1.375c0 3.354.48 6.047 1.438 8.078L233.516 135h-8.313zm-33.547 0l1.547-4.625h4.031c-.823-2.115-1.234-4.708-1.234-7.781v-1.672h-2.328l1.547-4.625h8.578c3.135 0 5.229.458 6.281 1.375a3.733 3.733 0 011.234 1.984c.198.792.297 2.177.297 4.156V135h-4.047v-9.063c0-2.416-.203-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-3.063v1.375c0 3.354.48 6.047 1.438 8.078L199.969 135h-8.313zm-18.265 0l-1.766-14.078h-2.516l1.547-4.625h11.078c2.094 0 3.578.078 4.453.234.876.157 1.573.469 2.094.938.875.791 1.313 2.047 1.313 3.765 0 2.292-.662 4.506-1.985 6.641s-3.104 3.854-5.343 5.156c-2.26 1.313-5.22 1.969-8.875 1.969zm3.453-4.625c2.333-.177 4.088-.687 5.265-1.531 1.938-1.407 2.907-3.25 2.907-5.531 0-.938-.328-1.57-.985-1.899-.656-.328-1.948-.492-3.875-.492h-4.469l1.157 9.453zM162.297 135v-9.063c0-2.416-.203-3.856-.61-4.32-.406-.463-1.682-.695-3.828-.695h-5.89l1.531-4.625h5.047c3.135 0 5.229.458 6.281 1.375a3.773 3.773 0 011.219 1.992c.198.797.297 2.18.297 4.148V135h-4.047zm-14.047-6.266h-4.063c.73-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.954-.719-.479-1.963-.895-3.734-1.25l2.094-3.89c2.552.51 4.33 1.21 5.335 2.101 1.006.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM122.375 135l-2.234-17.688 3.921-1.39 1.797 14.453c2.667 0 4.795-.615 6.383-1.844 1.589-1.229 2.383-2.87 2.383-4.922 0-2.24-1.208-3.359-3.625-3.359-.646 0-1.339.104-2.078.313l1.406-4.36a9.102 9.102 0 012.234-.297c1.917 0 3.415.576 4.493 1.727 1.078 1.15 1.617 2.747 1.617 4.789 0 2.292-.698 4.437-2.094 6.437s-3.292 3.568-5.687 4.703c-2.01.959-4.85 1.438-8.516 1.438zm222.344 38l2.265-8.688c.417-1.593.625-2.692.625-3.296 0-1.24-1.057-2.907-3.171-5l3.609-2.094c1.146.958 2.125 2.265 2.937 3.922 1.667-2.615 3.667-3.922 6-3.922 1.76 0 3.167.67 4.22 2.008 1.051 1.338 1.676 3.257 1.874 5.757l.531 6.688-1.53 4.625h-9.032l1.547-4.625h4.922l-.36-4.797c-.25-3.354-1.349-5.031-3.297-5.031-1.125 0-2.07.56-2.836 1.68-.765 1.12-1.497 3.007-2.195 5.664L348.953 173h-4.234zm-19.344 0l1.39-4.234h10.47v-4.157c0-1.948-.193-3.205-.579-3.773-.385-.568-1.385-1.065-3-1.492l-2.984-.781-.235.671c-.135.396-.203.74-.203 1.032 0 1.01.87 1.583 2.61 1.718l-1.078 3.25c-3.5-.323-5.25-1.671-5.25-4.047 0-.916.375-2.437 1.125-4.562l.968-2.703 7.125 1.734c2.313.563 3.815 1.284 4.508 2.164.693.88 1.04 2.503 1.04 4.868v6.078L339.874 173h-14.5zm-7.14 0v-9.594c0-1.927-.32-3.252-.962-3.976-.64-.724-1.997-1.295-4.07-1.711l2-3.797c2.448.448 4.208 1.15 5.281 2.11.709.635 1.185 1.385 1.43 2.25.245.864.367 2.25.367 4.155V173h-4.047zm-11.844 0v-9.063c0-2.416-.203-3.856-.61-4.32-.406-.463-1.682-.695-3.828-.695h-5.89l1.53-4.625h5.048c3.135 0 5.229.458 6.28 1.375a3.773 3.773 0 011.22 1.992c.198.797.296 2.18.296 4.149V173h-4.046zm-30.516 0l-2.203-17.5 3.922-1.578 1.172 9.25c1.135-.281 1.932-.945 2.39-1.992.459-1.047.688-2.716.688-5.008v-1.875h4.344v1.719c0 3.093-.566 5.445-1.696 7.054-1.13 1.61-2.92 2.607-5.367 2.993l.281 2.312c2.719 0 4.89-.453 6.516-1.36 2.844-1.583 4.265-5.192 4.265-10.827v-1.891h4.329v1.562c0 5.615-1.581 9.875-4.743 12.782-3.161 2.906-7.794 4.359-13.898 4.359zM250 173l1.547-4.625h4.031c-.823-2.115-1.234-4.708-1.234-7.781v-1.672h-2.328l1.547-4.625h8.578c3.135 0 5.229.458 6.28 1.375a3.733 3.733 0 011.235 1.984c.198.792.297 2.177.297 4.156V173h-4.047v-9.063c0-2.416-.203-3.856-.61-4.32-.405-.463-1.681-.695-3.827-.695h-3.063v1.375c0 3.354.48 6.047 1.438 8.078L258.313 173H250zm-31.422 0l2.266-8.688c.416-1.593.625-2.692.625-3.296 0-1.24-1.058-2.907-3.172-5l3.61-2.094c1.145.958 2.124 2.265 2.937 3.922 1.666-2.615 3.666-3.922 6-3.922 1.76 0 3.166.67 4.219 2.008 1.052 1.338 1.677 3.257 1.875 5.757l.53 6.688-1.53 4.625h-9.032l1.547-4.625h4.922l-.36-4.797c-.25-3.354-1.348-5.031-3.296-5.031-1.125 0-2.07.56-2.836 1.68-.766 1.12-1.498 3.007-2.196 5.664L222.813 173h-4.234zm-19.25 0l1.547-4.625h9.64v-4.438c0-2.416-.202-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-5.828l1.547-4.625h4.953c3.135 0 5.23.458 6.281 1.375a3.734 3.734 0 011.235 1.992c.198.797.297 2.18.297 4.149v6.562h2.5L215.53 173h-16.203zm-7.516 0v-9.594c0-1.927-.32-3.252-.96-3.976-.641-.724-1.998-1.295-4.07-1.711l2-3.797c2.447.448 4.208 1.15 5.28 2.11.709.635 1.185 1.385 1.43 2.25.245.864.367 2.25.367 4.155V173h-4.047zm-23.015 0l-1.766-14.078h-2.515l1.547-4.625h11.078c2.093 0 3.578.078 4.453.234.875.157 1.573.469 2.094.938.875.791 1.312 2.047 1.312 3.765 0 2.292-.661 4.506-1.984 6.641s-3.105 3.854-5.344 5.156c-2.26 1.313-5.219 1.969-8.875 1.969zm3.453-4.625c2.333-.177 4.089-.687 5.266-1.531 1.937-1.407 2.906-3.25 2.906-5.531 0-.938-.328-1.57-.984-1.899-.657-.328-1.948-.492-3.875-.492h-4.47l1.157 9.453zM146.531 173l-1.765-14.078h-2.516l1.547-4.625h11.078c2.094 0 3.578.078 4.453.234.875.157 1.573.469 2.094.938.875.791 1.312 2.047 1.312 3.765 0 2.292-.661 4.506-1.984 6.641s-3.104 3.854-5.344 5.156c-2.26 1.313-5.218 1.969-8.875 1.969zm3.453-4.625c2.334-.177 4.089-.687 5.266-1.531 1.938-1.407 2.906-3.25 2.906-5.531 0-.938-.328-1.57-.984-1.899-.656-.328-1.948-.492-3.875-.492h-4.469l1.156 9.453zM119.531 173l1.547-4.625h4.031c-.823-2.115-1.234-4.708-1.234-7.781v-1.672h-2.328l1.547-4.625h8.578c3.135 0 5.229.458 6.281 1.375a3.733 3.733 0 011.234 1.984c.198.792.297 2.177.297 4.156V173h-4.047v-9.063c0-2.416-.203-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-3.063v1.375c0 3.354.48 6.047 1.438 8.078L127.844 173h-8.313zm288.766 38v-.516c0-2.677.281-4.971.844-6.882.562-1.912 1.51-3.8 2.843-5.665l-.703-.671c-1.135-1.063-2.161-1.948-3.078-2.657l3.688-2.687c2.448 1.937 5.036 4.51 7.765 7.719.625-1.084 1.04-2.032 1.242-2.844.204-.813.305-1.927.305-3.344v-1.156h4.328v.906c0 3.521-1.182 6.568-3.547 9.14a41.618 41.618 0 014.422 6.47l-3.484 2.578c-2.063-3.709-4.865-7.355-8.406-10.938-1.26 1.958-1.891 4.948-1.891 8.969V211h-4.328zm-8.438 0v-9.594c0-1.927-.32-3.252-.96-3.976-.641-.724-1.998-1.295-4.07-1.711l2-3.797c2.447.448 4.207 1.15 5.28 2.11.709.635 1.185 1.385 1.43 2.25.245.864.367 2.25.367 4.155V211h-4.047zm-23.718 0l1.546-4.625h9.641v-4.438c0-2.416-.203-3.856-.61-4.32-.406-.463-1.682-.695-3.827-.695h-5.829l1.547-4.625h4.954c3.135 0 5.229.458 6.28 1.375a3.734 3.734 0 011.235 1.992c.198.797.297 2.18.297 4.149v6.562h2.5L392.344 211H376.14zm-4.016-6.266h-4.063c.73-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.953-.719-.48-1.963-.896-3.734-1.25l2.094-3.891c2.552.51 4.33 1.21 5.335 2.101 1.006.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zm-11.594 0h-4.062c.729-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.079-1.953-.718-.48-1.963-.896-3.734-1.25l2.094-3.891c2.552.51 4.33 1.21 5.336 2.101 1.005.891 1.507 2.206 1.507 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zm-27.328 10.891v-14.61h4.047v14.61h-4.047zm-2.515-18.703l1.546-4.625h10.844c1.99 0 3.39.088 4.203.266.813.177 1.495.541 2.047 1.093.802.813 1.203 1.98 1.203 3.5 0 3.052-1.12 5.901-3.36 8.547-2.239 2.646-5.13 4.537-8.671 5.672l1.672-4.969c1.656-.469 3.036-1.383 4.14-2.742 1.105-1.36 1.657-2.82 1.657-4.383 0-.937-.328-1.565-.985-1.883-.656-.317-1.958-.476-3.906-.476h-10.39zM312.594 211l-2.235-17.688 3.922-1.39 1.797 14.453c2.667 0 4.794-.615 6.383-1.844 1.588-1.229 2.383-2.87 2.383-4.922 0-2.24-1.209-3.359-3.625-3.359-.646 0-1.339.104-2.078.313l1.406-4.36a9.102 9.102 0 012.234-.297c1.917 0 3.414.576 4.492 1.727 1.079 1.15 1.618 2.747 1.618 4.789 0 2.292-.698 4.437-2.094 6.437s-3.292 3.568-5.688 4.703c-2.01.959-4.849 1.438-8.515 1.438zm-6.516-6.266h-4.062c.729-2.666 1.093-4.573 1.093-5.718 0-.823-.359-1.474-1.078-1.953-.719-.48-1.963-.896-3.734-1.25l2.094-3.891c2.552.51 4.33 1.21 5.336 2.101 1.005.891 1.507 2.206 1.507 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM277.766 211v-14.078h-2.485l1.547-4.625h10.485c3.135 0 5.229.458 6.28 1.375a3.734 3.734 0 011.235 1.992c.198.797.297 2.18.297 4.149v6.562L293.578 211h-15.812zm4.062-4.625h9.25v-4.438c0-2.427-.203-3.87-.61-4.328-.406-.458-1.682-.687-3.827-.687h-4.813v9.453zM243.86 211v-10.031h4.047V211h-4.047zm13.594 0v-9.063c0-2.416-.206-3.856-.617-4.32-.412-.463-1.685-.695-3.82-.695h-12.047l1.547-4.625h11.172c3.135 0 5.229.458 6.28 1.375a3.733 3.733 0 011.235 1.984c.198.792.297 2.177.297 4.156V211h-4.047zm-37.594 0l2.266-8.688c.417-1.593.625-2.692.625-3.296 0-1.24-1.057-2.907-3.172-5l3.61-2.094c1.145.958 2.124 2.265 2.937 3.922 1.667-2.615 3.667-3.922 6-3.922 1.76 0 3.167.67 4.219 2.008 1.052 1.338 1.677 3.257 1.875 5.757l.531 6.688-1.531 4.625h-9.031l1.546-4.625h4.922l-.36-4.797c-.25-3.354-1.348-5.031-3.296-5.031-1.125 0-2.07.56-2.836 1.68-.766 1.12-1.497 3.007-2.195 5.664L224.094 211h-4.235zm-7.453 0v-9.594c0-1.927-.32-3.252-.96-3.976-.641-.724-1.998-1.295-4.071-1.711l2-3.797c2.448.448 4.208 1.15 5.281 2.11.709.635 1.185 1.385 1.43 2.25.245.864.367 2.25.367 4.155V211h-4.047zm-27.75 0l1.547-4.625h4.031c-.823-2.115-1.234-4.708-1.234-7.781v-1.672h-2.328l1.547-4.625h8.578c3.135 0 5.229.458 6.281 1.375a3.733 3.733 0 011.234 1.984c.198.792.297 2.177.297 4.156V211h-4.047v-9.063c0-2.416-.203-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-3.063v1.375c0 3.354.48 6.047 1.438 8.078L192.969 211h-8.313zm-19.703 0v-.516c0-2.677.281-4.971.844-6.882.562-1.912 1.51-3.8 2.844-5.665l-.703-.671c-1.136-1.063-2.162-1.948-3.079-2.657l3.688-2.687c2.448 1.937 5.036 4.51 7.766 7.719.625-1.084 1.039-2.032 1.242-2.844.203-.813.304-1.927.304-3.344v-1.156h4.328v.906c0 3.521-1.182 6.568-3.546 9.14a41.618 41.618 0 014.422 6.47l-3.485 2.578c-2.062-3.709-4.864-7.355-8.406-10.938-1.26 1.958-1.89 4.948-1.89 8.969V211h-4.329zm-22.14 0l2.265-8.688c.417-1.593.625-2.692.625-3.296 0-1.24-1.057-2.907-3.172-5l3.61-2.094c1.145.958 2.125 2.265 2.937 3.922 1.667-2.615 3.667-3.922 6-3.922 1.76 0 3.167.67 4.219 2.008 1.052 1.338 1.677 3.257 1.875 5.757l.531 6.688-1.531 4.625h-9.031l1.547-4.625h4.921l-.359-4.797c-.25-3.354-1.349-5.031-3.297-5.031-1.125 0-2.07.56-2.836 1.68-.765 1.12-1.497 3.007-2.195 5.664L147.047 211h-4.234zm-23.282 0l1.547-4.625h4.031c-.823-2.115-1.234-4.708-1.234-7.781v-1.672h-2.328l1.547-4.625h8.578c3.135 0 5.229.458 6.281 1.375a3.733 3.733 0 011.234 1.984c.198.792.297 2.177.297 4.156V211h-4.047v-9.063c0-2.416-.203-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-3.063v1.375c0 3.354.48 6.047 1.438 8.078L127.844 211h-8.313zm318.844 38.39l1.594-4.718c2.041-.23 3.687-.839 4.937-1.828 1.948-1.542 2.922-3.406 2.922-5.594 0-.906-.32-1.52-.96-1.844-.641-.323-1.873-.484-3.696-.484h-5.563v-9.625l4.047-1.36v6.36h4.063c2.5 0 4.242.36 5.226 1.078.985.719 1.477 1.984 1.477 3.797 0 3.437-1.318 6.49-3.953 9.156-2.636 2.667-6 4.354-10.094 5.063zm-21.86-.39l-2.202-17.5 3.921-1.578 1.172 9.25c1.136-.281 1.933-.945 2.39-1.992.46-1.047.688-2.716.688-5.008v-1.875h4.344v1.719c0 3.093-.565 5.445-1.695 7.054-1.13 1.61-2.92 2.607-5.367 2.993l.28 2.312c2.72 0 4.891-.453 6.517-1.36 2.843-1.583 4.265-5.192 4.265-10.827v-1.891h4.328v1.562c0 5.615-1.58 9.875-4.742 12.782-3.161 2.906-7.794 4.359-13.898 4.359zm-6.546-6.266h-4.063c.73-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.953-.719-.48-1.964-.896-3.735-1.25l2.094-3.891c2.552.51 4.331 1.21 5.336 2.101s1.508 2.206 1.508 3.946c0 1.094-.25 2.76-.75 5l-.406 1.765zm-24.016 6.657l1.594-4.72c2.042-.228 3.687-.838 4.937-1.827 1.948-1.542 2.922-3.406 2.922-5.594 0-.906-.32-1.52-.96-1.844-.641-.323-1.873-.484-3.696-.484h-5.563v-9.625l4.047-1.36v6.36h4.063c2.5 0 4.242.36 5.226 1.078.985.719 1.477 1.984 1.477 3.797 0 3.437-1.318 6.49-3.953 9.156-2.636 2.667-6 4.354-10.094 5.063zm-8.89-.391v-9.594c0-1.927-.32-3.252-.961-3.976-.641-.724-1.998-1.295-4.07-1.711l2-3.797c2.447.448 4.208 1.15 5.28 2.11.709.635 1.185 1.385 1.43 2.25.245.864.367 2.25.367 4.155V249h-4.046zm-23.72 0l1.548-4.625h9.64v-4.438c0-2.416-.203-3.856-.61-4.32-.405-.463-1.681-.695-3.827-.695h-5.828l1.546-4.625h4.954c3.135 0 5.229.458 6.28 1.375a3.734 3.734 0 011.235 1.992c.198.797.297 2.18.297 4.149v6.562h2.5L369.547 249h-16.203zm-29.859 0l1.547-4.625h9.64v-4.438c0-2.416-.202-3.856-.608-4.32-.407-.463-1.683-.695-3.829-.695h-5.828l1.547-4.625h4.953c3.136 0 5.23.458 6.281 1.375a3.734 3.734 0 011.235 1.992c.198.797.297 2.18.297 4.149v6.562h2.5L339.688 249h-16.204zm-20.046 0v-.516c0-2.677.28-4.971.843-6.882.563-1.912 1.51-3.8 2.844-5.665l-.703-.671c-1.136-1.063-2.162-1.948-3.078-2.657l3.687-2.687c2.448 1.937 5.037 4.51 7.766 7.719.625-1.084 1.039-2.032 1.242-2.844.203-.813.305-1.927.305-3.344v-1.156h4.328v.906c0 3.521-1.182 6.568-3.547 9.14a41.618 41.618 0 014.422 6.47l-3.485 2.578c-2.062-3.709-4.864-7.355-8.406-10.938-1.26 1.958-1.89 4.948-1.89 8.969V249h-4.329zm-24.266 0l1.547-4.625h4.031c-.823-2.115-1.234-4.708-1.234-7.781v-1.672h-2.329l1.547-4.625h8.579c3.135 0 5.229.458 6.28 1.375a3.733 3.733 0 011.235 1.984c.198.792.297 2.177.297 4.156V249h-4.047v-9.063c0-2.416-.203-3.856-.61-4.32-.406-.463-1.682-.695-3.827-.695h-3.063v1.375c0 3.354.48 6.047 1.438 8.078L287.484 249h-8.312zm-7.094 0v-9.063c0-2.416-.203-3.856-.61-4.32-.406-.463-1.682-.695-3.827-.695h-5.891l1.531-4.625h5.047c3.136 0 5.23.458 6.281 1.375a3.773 3.773 0 011.22 1.992c.197.797.296 2.18.296 4.149V249h-4.047zm-14.047-6.266h-4.062c.729-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.079-1.953-.718-.48-1.963-.896-3.734-1.25l2.094-3.891c2.552.51 4.33 1.21 5.336 2.101 1.005.891 1.507 2.206 1.507 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM219.86 249v-.516c0-2.677.282-4.971.844-6.882.563-1.912 1.51-3.8 2.844-5.665l-.703-.671c-1.136-1.063-2.162-1.948-3.078-2.657l3.687-2.687c2.448 1.937 5.037 4.51 7.766 7.719.625-1.084 1.039-2.032 1.242-2.844.203-.813.305-1.927.305-3.344v-1.156h4.328v.906c0 3.521-1.183 6.568-3.547 9.14a41.618 41.618 0 014.422 6.47l-3.485 2.578c-2.062-3.709-4.864-7.355-8.406-10.938-1.26 1.958-1.89 4.948-1.89 8.969V249h-4.329zm-4.937-6.266h-4.063c.73-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.953-.719-.48-1.964-.896-3.734-1.25l2.093-3.891c2.552.51 4.331 1.21 5.336 2.101 1.006.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM192.75 249l1.563-4.625h5.515v-6.078c0-1.646-.354-2.724-1.062-3.234-.948-.678-2.224-1.245-3.828-1.704l1.843-3.437c1.73.427 3.297 1.047 4.703 1.86.907.53 1.532 1.182 1.875 1.952.344.771.516 1.922.516 3.453v7.188L202.328 249h-9.578zm-17.906 0l-2.235-17.688 3.922-1.39 1.797 14.453c2.667 0 4.794-.615 6.383-1.844 1.588-1.229 2.383-2.87 2.383-4.922 0-2.24-1.209-3.359-3.625-3.359-.646 0-1.339.104-2.078.313l1.406-4.36a9.102 9.102 0 012.234-.297c1.917 0 3.414.576 4.492 1.727 1.079 1.15 1.618 2.747 1.618 4.789 0 2.292-.698 4.437-2.094 6.437s-3.292 3.568-5.688 4.703c-2.01.959-4.849 1.438-8.515 1.438zm-9.938 0v-9.063c0-2.416-.203-3.856-.61-4.32-.405-.463-1.681-.695-3.827-.695h-5.89l1.53-4.625h5.047c3.136 0 5.23.458 6.281 1.375a3.773 3.773 0 011.22 1.992c.197.797.296 2.18.296 4.149V249h-4.047zm-24.625 0l1.563-4.625h5.515v-6.078c0-1.646-.354-2.724-1.062-3.234-.948-.678-2.224-1.245-3.828-1.704l1.844-3.437c1.729.427 3.296 1.047 4.703 1.86.906.53 1.53 1.182 1.875 1.952.343.771.515 1.922.515 3.453v7.188L149.86 249h-9.578zm-17.906 0l-2.234-17.688 3.921-1.39 1.797 14.453c2.667 0 4.795-.615 6.383-1.844 1.589-1.229 2.383-2.87 2.383-4.922 0-2.24-1.208-3.359-3.625-3.359-.646 0-1.339.104-2.078.313l1.406-4.36a9.102 9.102 0 012.234-.297c1.917 0 3.415.576 4.493 1.727 1.078 1.15 1.617 2.747 1.617 4.789 0 2.292-.698 4.437-2.094 6.437s-3.292 3.568-5.687 4.703c-2.01.959-4.85 1.438-8.516 1.438zm258.422 38v-9.594c0-1.927-.32-3.252-.961-3.976-.64-.724-1.997-1.295-4.07-1.711l2-3.797c2.448.448 4.208 1.15 5.28 2.11.71.635 1.186 1.385 1.43 2.25.245.864.368 2.25.368 4.156V287h-4.047zm-11.844 0v-9.063c0-2.416-.203-3.856-.61-4.32-.406-.463-1.682-.695-3.827-.695h-5.891l1.531-4.625h5.047c3.136 0 5.23.458 6.281 1.375a3.773 3.773 0 011.22 1.992c.197.797.296 2.18.296 4.149V287h-4.047zm-29.25 0l1.36-4.14h8.468c-3.833-3.709-6.713-8.162-8.64-13.36l4.093-1.578a28.973 28.973 0 004.938 8.875c1.469-1.781 2.203-3.943 2.203-6.485v-2.015h4.344v1.312c0 3.97-1.36 7.214-4.078 9.735 1.573 1.614 2.974 2.791 4.203 3.531l-1.39 4.125h-15.5zm-20.594 0v-10.031h4.047V287h-4.047zm13.594 0v-9.063c0-2.416-.206-3.856-.617-4.32-.412-.463-1.685-.695-3.82-.695h-12.047l1.547-4.625h11.171c3.136 0 5.23.458 6.282 1.375a3.733 3.733 0 011.234 1.984c.198.792.297 2.177.297 4.156V287h-4.047zm-48.61 2.781l1.563-4.672c1.667 0 3.459-.28 5.375-.843l-3.36-14.985 3.876-1.36 3.265 14.563c1.636-1.24 2.774-2.78 3.415-4.625.64-1.843.96-4.484.96-7.921v-1.641h4.344v1.156c0 3.99-.612 7.318-1.836 9.985-1.224 2.666-3.153 4.88-5.789 6.64-3.698 2.469-7.635 3.703-11.812 3.703zm-14.796-2.39l1.594-4.72c2.041-.228 3.687-.838 4.937-1.827 1.948-1.542 2.922-3.406 2.922-5.594 0-.906-.32-1.52-.96-1.844-.642-.323-1.873-.484-3.696-.484h-5.563v-9.625l4.047-1.36v6.36h4.063c2.5 0 4.242.36 5.226 1.078.985.719 1.477 1.984 1.477 3.797 0 3.437-1.318 6.49-3.953 9.156-2.636 2.667-6 4.354-10.094 5.063zm-15.938-6.657h-4.062c.729-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.954-.72-.479-1.964-.895-3.735-1.25l2.094-3.89c2.552.51 4.33 1.21 5.336 2.101 1.005.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.407 1.765zM236.656 287v-14.078h-9.531l1.547-4.625h16.094l-1.547 4.625h-2.516V287h-4.047zm-12.86-6.266h-4.062c.73-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.954-.719-.479-1.964-.895-3.734-1.25l2.093-3.89c2.552.51 4.331 1.21 5.336 2.101 1.006.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM196.547 287v-14.078h-9.53l1.547-4.625h16.093l-1.547 4.625h-2.515V287h-4.047zm-28.25 0l1.392-4.234h10.468v-4.157c0-1.948-.192-3.205-.578-3.773-.385-.568-1.385-1.065-3-1.492l-2.984-.781-.235.671c-.135.396-.203.74-.203 1.032 0 1.01.87 1.583 2.61 1.718l-1.078 3.25c-3.5-.323-5.25-1.671-5.25-4.046 0-.917.374-2.438 1.125-4.563l.968-2.703 7.125 1.734c2.313.563 3.815 1.284 4.508 2.164.693.88 1.04 2.503 1.04 4.868v6.078L182.796 287h-14.5zm-8.75 0v-14.078h-9.53l1.547-4.625h16.093l-1.547 4.625h-2.515V287h-4.047zm-28.25 0l1.392-4.234h10.468v-4.157c0-1.948-.192-3.205-.578-3.773-.385-.568-1.385-1.065-3-1.492l-2.984-.781-.235.671c-.135.396-.203.74-.203 1.032 0 1.01.87 1.583 2.61 1.718l-1.078 3.25c-3.5-.323-5.25-1.671-5.25-4.046 0-.917.374-2.438 1.125-4.563l.968-2.703 7.125 1.734c2.313.563 3.815 1.284 4.508 2.164.693.88 1.04 2.503 1.04 4.868v6.078L145.796 287h-14.5zm-7.155 4.625v-14.281c0-2.032-.36-3.344-1.079-3.938-1.041-.875-2.328-1.552-3.859-2.031l1.86-3.453c1.875.562 3.53 1.37 4.968 2.422.834.614 1.401 1.364 1.703 2.25.302.885.454 2.255.454 4.11v14.921h-4.047zM426.797 325v-10.031h4.047V325h-4.047zm13.594 0v-9.063c0-2.416-.206-3.856-.618-4.32-.411-.463-1.684-.695-3.82-.695h-12.047l1.547-4.625h11.172c3.135 0 5.23.458 6.281 1.375a3.733 3.733 0 011.235 1.984c.198.792.296 2.177.296 4.156V325h-4.046zm-36.422 0v-.516c0-2.677.281-4.971.844-6.882.562-1.912 1.51-3.8 2.843-5.664l-.703-.672c-1.135-1.063-2.161-1.948-3.078-2.657l3.688-2.687c2.447 1.937 5.036 4.51 7.765 7.719.625-1.084 1.04-2.032 1.242-2.844.203-.813.305-1.927.305-3.344v-1.156h4.328v.906c0 3.521-1.182 6.568-3.547 9.14a41.618 41.618 0 014.422 6.47l-3.484 2.578c-2.063-3.709-4.865-7.355-8.406-10.938-1.26 1.958-1.891 4.948-1.891 8.969V325h-4.328zm-4.938-6.266h-4.062c.729-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.079-1.954-.718-.479-1.963-.895-3.734-1.25l2.094-3.89c2.552.51 4.33 1.21 5.336 2.101 1.005.891 1.507 2.206 1.507 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM376.86 325l1.563-4.625h5.515v-6.078c0-1.646-.354-2.724-1.062-3.235-.948-.677-2.224-1.244-3.828-1.703l1.844-3.437c1.729.427 3.296 1.047 4.703 1.86.906.53 1.531 1.182 1.875 1.952.344.771.515 1.922.515 3.454v7.187L386.438 325h-9.579zm-17.906 0l-2.234-17.688 3.922-1.39 1.796 14.453c2.667 0 4.795-.615 6.383-1.844 1.589-1.229 2.383-2.87 2.383-4.922 0-2.24-1.208-3.359-3.625-3.359-.646 0-1.338.104-2.078.313l1.406-4.36a9.102 9.102 0 012.235-.297c1.916 0 3.414.576 4.492 1.727 1.078 1.15 1.617 2.747 1.617 4.789 0 2.292-.698 4.437-2.094 6.437s-3.291 3.568-5.687 4.704c-2.01.958-4.85 1.437-8.516 1.437zm-9.937 0v-9.063c0-2.416-.203-3.856-.61-4.32-.406-.463-1.682-.695-3.828-.695h-5.89l1.53-4.625h5.048c3.135 0 5.229.458 6.28 1.375a3.773 3.773 0 011.22 1.992c.198.797.296 2.18.296 4.149V325h-4.046zm-24.625 0l1.562-4.625h5.516v-6.078c0-1.646-.354-2.724-1.063-3.235-.948-.677-2.224-1.244-3.828-1.703l1.844-3.437c1.729.427 3.297 1.047 4.703 1.86.906.53 1.531 1.182 1.875 1.952.344.771.516 1.922.516 3.454v7.187L333.969 325h-9.578zm-17.907 0l-2.234-17.688 3.922-1.39 1.797 14.453c2.666 0 4.794-.615 6.383-1.844 1.588-1.229 2.382-2.87 2.382-4.922 0-2.24-1.208-3.359-3.625-3.359-.645 0-1.338.104-2.078.313l1.406-4.36a9.102 9.102 0 012.235-.297c1.917 0 3.414.576 4.492 1.727 1.078 1.15 1.617 2.747 1.617 4.789 0 2.292-.698 4.437-2.094 6.437-1.395 2-3.291 3.568-5.687 4.704-2.01.958-4.849 1.437-8.516 1.437zm-32.359 0l1.547-4.625h9.64v-4.438c0-2.416-.203-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-5.828l1.547-4.625h4.953c3.135 0 5.229.458 6.281 1.375a3.734 3.734 0 011.235 1.992c.197.797.296 2.18.296 4.149v6.562h2.5L290.33 325h-16.204zm-19.219 0l1.36-4.14h8.468c-3.833-3.709-6.713-8.162-8.64-13.36l4.094-1.578a28.973 28.973 0 004.937 8.875c1.469-1.781 2.203-3.943 2.203-6.485v-2.015h4.344v1.312c0 3.97-1.36 7.214-4.078 9.735 1.573 1.614 2.974 2.791 4.203 3.531l-1.39 4.125h-15.5zm-8.687 0v-14.078h-9.531l1.546-4.625h16.094l-1.547 4.625h-2.515V325h-4.047zm-40.422 0v-10.031h4.047V325h-4.047zm13.594 0v-9.063c0-2.416-.206-3.856-.618-4.32-.411-.463-1.684-.695-3.82-.695h-12.047l1.547-4.625h11.172c3.135 0 5.23.458 6.281 1.375a3.733 3.733 0 011.235 1.984c.198.792.297 2.177.297 4.156V325h-4.047zm-32.813.39l1.594-4.718c2.042-.23 3.687-.839 4.937-1.828 1.948-1.542 2.922-3.406 2.922-5.594 0-.906-.32-1.52-.96-1.844-.641-.323-1.873-.484-3.696-.484h-5.563v-9.625l4.047-1.36v6.36h4.063c2.5 0 4.242.36 5.226 1.078.985.719 1.477 1.984 1.477 3.797 0 3.437-1.318 6.49-3.953 9.156-2.636 2.667-6 4.354-10.094 5.063zm-21.125 4.235v-14.61h4.047v14.61h-4.047zm-2.516-18.703l1.547-4.625h10.844c1.99 0 3.39.088 4.203.265.813.178 1.495.542 2.047 1.094.802.813 1.203 1.98 1.203 3.5 0 3.052-1.12 5.901-3.36 8.547-2.239 2.646-5.13 4.537-8.671 5.672l1.672-4.969c1.656-.469 3.036-1.383 4.14-2.742 1.105-1.36 1.657-2.82 1.657-4.383 0-.937-.328-1.565-.985-1.883-.656-.317-1.958-.476-3.906-.476h-10.39zM155.625 325v-9.594c0-1.927-.32-3.252-.96-3.976-.642-.724-1.998-1.295-4.071-1.711l2-3.797c2.448.448 4.208 1.15 5.281 2.11.708.635 1.185 1.385 1.43 2.25.244.864.367 2.25.367 4.156V325h-4.047zm-25.516 0v-14.078h-2.53l1.562-4.625h10.89c3.136 0 5.23.458 6.281 1.375a3.734 3.734 0 011.235 1.992c.198.797.297 2.18.297 4.149V325h-4.047v-9.063c0-2.416-.203-3.856-.61-4.32-.406-.463-1.682-.695-3.828-.695h-5.203V325h-4.047zm-9.656 0v-5.016h5.016V325h-5.016zm249.906 38v-10.031h4.047V363h-4.047zm13.594 0v-9.063c0-2.416-.206-3.856-.617-4.32-.412-.463-1.685-.695-3.82-.695h-12.047l1.547-4.625h11.171c3.136 0 5.23.458 6.282 1.375a3.733 3.733 0 011.234 1.984c.198.792.297 2.177.297 4.156V363h-4.047zm-36.86 0l-2.202-17.5 3.921-1.578 1.172 9.25c1.136-.281 1.933-.945 2.391-1.992.458-1.047.688-2.716.688-5.008v-1.875h4.343v1.719c0 3.093-.565 5.445-1.695 7.054-1.13 1.61-2.92 2.607-5.367 2.993l.281 2.312c2.719 0 4.89-.453 6.516-1.36 2.843-1.583 4.265-5.192 4.265-10.827v-1.891h4.328v1.562c0 5.615-1.58 9.875-4.742 12.782-3.161 2.906-7.794 4.359-13.898 4.359zm-21.937 0l1.39-4.234h10.47v-4.157c0-1.948-.193-3.205-.579-3.773-.385-.568-1.385-1.065-3-1.492l-2.984-.781-.234.671c-.136.396-.203.74-.203 1.032 0 1.01.87 1.583 2.609 1.718l-1.078 3.25c-3.5-.323-5.25-1.671-5.25-4.046 0-.917.375-2.438 1.125-4.563l.969-2.703 7.125 1.734c2.312.563 3.815 1.284 4.507 2.164.693.88 1.04 2.503 1.04 4.868v6.078L339.656 363h-14.5zm-20.656 0v-10.031h4.047V363H304.5zm13.594 0v-9.063c0-2.416-.206-3.856-.617-4.32-.412-.463-1.685-.695-3.82-.695h-12.048l1.547-4.625h11.172c3.136 0 5.23.458 6.281 1.375a3.733 3.733 0 011.235 1.984c.198.792.297 2.177.297 4.156V363h-4.047zm-48.14 0l2.265-8.688c.416-1.593.625-2.692.625-3.296 0-1.24-1.058-2.907-3.172-5l3.61-2.094c1.145.958 2.124 2.265 2.937 3.922 1.666-2.615 3.666-3.922 6-3.922 1.76 0 3.166.67 4.219 2.008 1.052 1.338 1.677 3.257 1.875 5.757l.53 6.688-1.53 4.625h-9.032l1.547-4.625h4.922l-.36-4.797c-.25-3.354-1.348-5.031-3.296-5.031-1.125 0-2.07.56-2.836 1.68-.766 1.12-1.498 3.007-2.195 5.664L274.188 363h-4.235zm-7.376 0v-9.063c0-2.416-.203-3.856-.61-4.32-.406-.463-1.682-.695-3.827-.695h-5.891l1.531-4.625h5.047c3.136 0 5.23.458 6.281 1.375a3.773 3.773 0 011.22 1.992c.197.797.296 2.18.296 4.149V363h-4.047zm-31.14 0v-14.078h-2.532l1.563-4.625h10.89c3.136 0 5.23.458 6.282 1.375a3.734 3.734 0 011.234 1.992c.198.797.297 2.18.297 4.149V363h-4.047v-9.063c0-2.416-.203-3.856-.61-4.32-.406-.463-1.682-.695-3.827-.695h-5.204V363h-4.047zm-6.344-6.266h-4.063c.73-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.954-.719-.479-1.964-.895-3.734-1.25l2.093-3.89c2.552.51 4.331 1.21 5.336 2.101s1.508 2.206 1.508 3.946c0 1.094-.25 2.76-.75 5l-.406 1.765zM198.204 363l1.546-4.625h9.64v-4.438c0-2.416-.202-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-5.828l1.547-4.625h4.953c3.135 0 5.23.458 6.281 1.375a3.734 3.734 0 011.235 1.992c.198.797.297 2.18.297 4.149v6.562h2.5L214.405 363h-16.203zm-21.032 0v-10.031h4.047V363h-4.047zm13.594 0v-9.063c0-2.416-.206-3.856-.618-4.32-.411-.463-1.684-.695-3.82-.695h-12.047l1.547-4.625H187c3.135 0 5.23.458 6.281 1.375a3.733 3.733 0 011.235 1.984c.198.792.297 2.177.297 4.156V363h-4.047zm-46.97 0v-.516c0-2.677.282-4.971.845-6.882.562-1.912 1.51-3.8 2.843-5.664l-.703-.672c-1.135-1.063-2.161-1.948-3.078-2.657l3.688-2.687c2.448 1.937 5.036 4.51 7.765 7.719.625-1.084 1.04-2.032 1.242-2.844.204-.813.305-1.927.305-3.344v-1.156h4.328v.906c0 3.521-1.182 6.568-3.547 9.14a41.618 41.618 0 014.422 6.47l-3.484 2.578c-2.063-3.709-4.865-7.355-8.406-10.938-1.26 1.958-1.891 4.948-1.891 8.969V363h-4.328zm-24.265 0l1.547-4.625h4.031c-.823-2.115-1.234-4.708-1.234-7.781v-1.672h-2.328l1.547-4.625h8.578c3.135 0 5.229.458 6.281 1.375a3.733 3.733 0 011.234 1.984c.198.792.297 2.177.297 4.156V363h-4.047v-9.063c0-2.416-.203-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-3.063v1.375c0 3.354.48 6.047 1.438 8.078L127.844 363h-8.313zm285.016 31.734h-4.063c.73-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.954-.719-.479-1.964-.895-3.734-1.25l2.093-3.89c2.552.51 4.331 1.21 5.336 2.101 1.006.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM378.812 401l1.532-4.625h8.734c.865-1.99 1.297-3.682 1.297-5.078 0-1.646-.38-2.787-1.14-3.422-.761-.635-2.13-.953-4.11-.953h-5.406l1.531-4.625h5.094c1.781 0 3.15.156 4.11.469.957.312 1.77.87 2.437 1.671 1.062 1.271 1.593 3.063 1.593 5.375 0 1.844-.49 4.032-1.468 6.563L391.5 401h-12.688zm-7.453 0v-9.594c0-1.927-.32-3.252-.96-3.976-.641-.724-1.998-1.295-4.07-1.711l2-3.797c2.447.448 4.207 1.15 5.28 2.11.709.635 1.185 1.385 1.43 2.25.245.864.367 2.25.367 4.156V401h-4.047zm-20.843.39l1.593-4.718c2.042-.23 3.688-.839 4.938-1.828 1.948-1.542 2.922-3.406 2.922-5.594 0-.906-.32-1.52-.961-1.844-.64-.323-1.873-.484-3.695-.484h-5.563v-9.625l4.047-1.36v6.36h4.062c2.5 0 4.243.36 5.227 1.078.984.719 1.476 1.984 1.476 3.797 0 3.437-1.317 6.49-3.953 9.156-2.635 2.667-6 4.354-10.093 5.063zm-8.891-.39v-9.594c0-1.927-.32-3.252-.96-3.976-.642-.724-1.998-1.295-4.071-1.711l2-3.797c2.448.448 4.208 1.15 5.281 2.11.708.635 1.185 1.385 1.43 2.25.244.864.367 2.25.367 4.156V401h-4.047zm-27.75 0l1.547-4.625h4.031c-.823-2.115-1.234-4.708-1.234-7.781v-1.672h-2.328l1.546-4.625h8.579c3.135 0 5.229.458 6.28 1.375a3.733 3.733 0 011.235 1.984c.198.792.297 2.177.297 4.156V401h-4.047v-9.063c0-2.416-.203-3.856-.61-4.32-.405-.463-1.681-.695-3.827-.695h-3.063v1.375c0 3.354.48 6.047 1.438 8.078L322.188 401h-8.313zm-30.688 0l-2.203-17.5 3.922-1.578 1.172 9.25c1.136-.281 1.932-.945 2.39-1.992.46-1.047.688-2.716.688-5.008v-1.875h4.344v1.719c0 3.093-.565 5.445-1.695 7.054-1.13 1.61-2.92 2.607-5.368 2.993l.282 2.312c2.719 0 4.89-.453 6.515-1.36 2.844-1.583 4.266-5.192 4.266-10.827v-1.891h4.328v1.562c0 5.615-1.58 9.875-4.742 12.782-3.162 2.906-7.794 4.359-13.899 4.359zm-21.937 0l1.39-4.234h10.47v-4.157c0-1.948-.193-3.205-.579-3.773-.385-.568-1.385-1.065-3-1.492l-2.984-.781-.235.671c-.135.396-.203.74-.203 1.032 0 1.01.87 1.583 2.61 1.718l-1.078 3.25c-3.5-.323-5.25-1.671-5.25-4.046 0-.917.375-2.438 1.125-4.563l.968-2.703 7.125 1.734c2.313.563 3.815 1.284 4.508 2.164.693.88 1.04 2.503 1.04 4.868v6.078L275.75 401h-14.5zm-22.969 0l1.547-4.625h4.031c-.823-2.115-1.234-4.708-1.234-7.781v-1.672h-2.328l1.547-4.625h8.578c3.135 0 5.229.458 6.281 1.375a3.733 3.733 0 011.235 1.984c.197.792.296 2.177.296 4.156V401h-4.046v-9.063c0-2.416-.204-3.856-.61-4.32-.406-.463-1.682-.695-3.828-.695h-3.063v1.375c0 3.354.48 6.047 1.438 8.078L246.594 401h-8.313zm-31.234 0v-10.031h4.047V401h-4.047zm13.594 0v-9.063c0-2.416-.206-3.856-.618-4.32-.411-.463-1.684-.695-3.82-.695h-12.047l1.547-4.625h11.172c3.135 0 5.23.458 6.281 1.375a3.733 3.733 0 011.235 1.984c.198.792.297 2.177.297 4.156V401h-4.047zm-39.72 0l1.548-4.625h4.031c-.823-2.115-1.234-4.708-1.234-7.781v-1.672h-2.328l1.546-4.625h8.578c3.136 0 5.23.458 6.282 1.375a3.733 3.733 0 011.234 1.984c.198.792.297 2.177.297 4.156V401h-4.047v-9.063c0-2.416-.203-3.856-.61-4.32-.406-.463-1.682-.695-3.827-.695h-3.063v1.375c0 3.354.48 6.047 1.438 8.078L189.234 401h-8.312zm-13.171.39l-1.594-3.859c2.334-1.135 4.724-2.724 7.172-4.765l-.39-2.485c-.23-1.427-.566-2.398-1.008-2.914-.443-.515-1.42-1.075-2.93-1.68l-.89-.359 1.75-3.406c2.562.844 4.317 1.687 5.265 2.531.625.563 1.055 1.177 1.29 1.844.233.667.517 2.094.85 4.281l.61 4c.448 2.938.89 5.078 1.328 6.422h-4.36c-.28-1.115-.577-2.594-.89-4.438-1.542 1.553-3.61 3.162-6.203 4.829zm-5.375-6.656h-4.063c.73-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.954-.719-.479-1.963-.895-3.734-1.25l2.094-3.89c2.552.51 4.33 1.21 5.335 2.101 1.006.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM147.281 401v-9.594c0-1.927-.32-3.252-.96-3.976-.641-.724-1.998-1.295-4.071-1.711l2-3.797c2.448.448 4.208 1.15 5.281 2.11.709.635 1.185 1.385 1.43 2.25.245.864.367 2.25.367 4.156V401h-4.047zm-27.75 0l1.547-4.625h4.031c-.823-2.115-1.234-4.708-1.234-7.781v-1.672h-2.328l1.547-4.625h8.578c3.135 0 5.229.458 6.281 1.375a3.733 3.733 0 011.234 1.984c.198.792.297 2.177.297 4.156V401h-4.047v-9.063c0-2.416-.203-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-3.063v1.375c0 3.354.48 6.047 1.438 8.078L127.844 401h-8.313zm218.406 38v-10.031h4.047V439h-4.046zm13.594 0v-9.063c0-2.416-.205-3.856-.617-4.32-.411-.463-1.685-.695-3.82-.695h-12.047l1.547-4.625h11.172c3.135 0 5.229.458 6.28 1.375a3.733 3.733 0 011.235 1.984c.198.792.297 2.177.297 4.156V439h-4.047zm-35.687 0l1.547-4.625h9.64v-4.438c0-2.416-.203-3.856-.61-4.32-.405-.463-1.681-.695-3.827-.695h-5.828l1.546-4.625h4.954c3.135 0 5.229.458 6.28 1.375a3.734 3.734 0 011.235 1.992c.198.797.297 2.18.297 4.149v6.562h2.5L332.047 439h-16.203zm-18.61 0l-1.765-14.078h-2.516l1.547-4.625h11.078c2.094 0 3.578.078 4.453.234.875.157 1.573.469 2.094.938.875.791 1.313 2.047 1.313 3.765 0 2.292-.662 4.506-1.985 6.641s-3.104 3.854-5.344 5.156c-2.26 1.313-5.218 1.969-8.875 1.969zm3.454-4.625c2.333-.177 4.088-.687 5.265-1.531 1.938-1.407 2.906-3.25 2.906-5.531 0-.938-.328-1.57-.984-1.899-.656-.328-1.948-.492-3.875-.492h-4.469l1.156 9.453zm-11.125-1.64H285.5c.73-2.667 1.094-4.574 1.094-5.72 0-.822-.36-1.473-1.078-1.952-.72-.48-1.964-.896-3.735-1.25l2.094-3.891c2.552.51 4.33 1.21 5.336 2.101 1.005.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM263.375 439l-1.766-14.078h-2.515l1.547-4.625h11.078c2.094 0 3.578.078 4.453.234.875.157 1.573.469 2.094.938.875.791 1.312 2.047 1.312 3.765 0 2.292-.661 4.506-1.984 6.641s-3.104 3.854-5.344 5.156c-2.26 1.313-5.219 1.969-8.875 1.969zm3.453-4.625c2.333-.177 4.089-.687 5.266-1.531 1.937-1.407 2.906-3.25 2.906-5.531 0-.938-.328-1.57-.984-1.899-.657-.328-1.948-.492-3.875-.492h-4.47l1.157 9.453zm-11.125-1.64h-4.062c.729-2.667 1.093-4.574 1.093-5.72 0-.822-.359-1.473-1.078-1.952-.719-.48-1.963-.896-3.734-1.25l2.094-3.891c2.552.51 4.33 1.21 5.336 2.101 1.005.891 1.507 2.206 1.507 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM224.781 439l1.547-4.625h4.031c-.823-2.115-1.234-4.708-1.234-7.781v-1.672h-2.328l1.547-4.625h8.578c3.135 0 5.229.458 6.281 1.375a3.733 3.733 0 011.234 1.984c.198.792.297 2.177.297 4.156V439h-4.047v-9.063c0-2.416-.203-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-3.063v1.375c0 3.354.48 6.047 1.438 8.078L233.094 439h-8.313zm-102.89 0v-23.125h4.812v9.39h9.563v-9.39h4.812V439h-4.812v-10.547h-9.563V439h-4.812zm30.703 0v-19.953h-8.281v-3.172h21.39v3.172h-8.281V439h-4.828zm16.344 0v-23.125h6.375l5.562 16.266 5.734-16.266h5.594V439h-4.437v-17.453l-5.641 15.906h-3.875l-5.531-16.14V439h-3.781zm29.046 0v-23.125h4.813v19.844h10.422V439h-15.235zm204.094 38v-9.594c0-1.927-.32-3.252-.96-3.976-.641-.724-1.998-1.295-4.071-1.711l2-3.797c2.448.448 4.208 1.15 5.281 2.11.708.635 1.185 1.385 1.43 2.25.245.864.367 2.25.367 4.156V477h-4.047zm-25.625 0l2.266-8.688c.416-1.593.625-2.692.625-3.296 0-1.24-1.058-2.907-3.172-5l3.61-2.094c1.145.958 2.124 2.265 2.937 3.922 1.666-2.615 3.666-3.922 6-3.922 1.76 0 3.166.67 4.219 2.008 1.052 1.338 1.677 3.257 1.875 5.757l.53 6.688-1.53 4.625h-9.032l1.547-4.625h4.922l-.36-4.797c-.25-3.354-1.348-5.031-3.296-5.031-1.125 0-2.07.56-2.836 1.68-.766 1.12-1.498 3.007-2.195 5.664L380.688 477h-4.235zm-19.984 0v-.516c0-2.677.281-4.971.844-6.882.562-1.912 1.51-3.8 2.843-5.664l-.703-.672c-1.135-1.063-2.161-1.948-3.078-2.657l3.688-2.687c2.447 1.937 5.036 4.51 7.765 7.719.625-1.084 1.04-2.032 1.242-2.844.203-.813.305-1.927.305-3.344v-1.156h4.328v.906c0 3.521-1.182 6.568-3.547 9.14a41.618 41.618 0 014.422 6.47l-3.484 2.578c-2.063-3.709-4.865-7.355-8.406-10.938-1.26 1.958-1.891 4.948-1.891 8.969V477h-4.328zm-20.328 0l1.39-4.234H348v-4.157c0-1.948-.193-3.205-.578-3.773-.386-.568-1.386-1.065-3-1.492l-2.985-.781-.234.671c-.135.396-.203.74-.203 1.032 0 1.01.87 1.583 2.61 1.718l-1.079 3.25c-3.5-.323-5.25-1.671-5.25-4.046 0-.917.375-2.438 1.125-4.563l.969-2.703 7.125 1.734c2.313.563 3.815 1.284 4.508 2.164.693.88 1.039 2.503 1.039 4.868v6.078L350.64 477h-14.5zm-20.11 0l-2.203-17.5 3.922-1.578 1.172 9.25c1.135-.281 1.932-.945 2.39-1.992.459-1.047.688-2.716.688-5.008v-1.875h4.344v1.719c0 3.093-.565 5.445-1.696 7.054-1.13 1.61-2.919 2.607-5.367 2.993l.281 2.312c2.72 0 4.891-.453 6.516-1.36 2.844-1.583 4.266-5.192 4.266-10.827v-1.891h4.328v1.562c0 5.615-1.58 9.875-4.742 12.782-3.162 2.906-7.795 4.359-13.899 4.359zm-9.969 0v-9.063c0-2.416-.203-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-5.89l1.53-4.625h5.048c3.135 0 5.229.458 6.28 1.375a3.773 3.773 0 011.22 1.992c.197.797.296 2.18.296 4.149V477h-4.046zm-33.375 0l1.547-4.625h4.032c-.823-2.115-1.235-4.708-1.235-7.781v-1.672h-2.328l1.547-4.625h8.578c3.136 0 5.23.458 6.281 1.375a3.733 3.733 0 011.235 1.984c.198.792.297 2.177.297 4.156V477h-4.047v-9.063c0-2.416-.203-3.856-.61-4.32-.406-.463-1.682-.695-3.828-.695h-3.062v1.375c0 3.354.479 6.047 1.437 8.078L281 477h-8.313zm-29.515 0l1.547-4.625h9.64v-4.438c0-2.416-.203-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-5.828l1.547-4.625h4.953c3.135 0 5.229.458 6.281 1.375a3.734 3.734 0 011.234 1.992c.198.797.297 2.18.297 4.149v6.562h2.5L259.375 477h-16.203zm-18.156 0l1.53-4.625h8.735c.865-1.99 1.297-3.682 1.297-5.078 0-1.646-.38-2.787-1.14-3.422-.76-.635-2.13-.953-4.11-.953h-5.406l1.531-4.625h5.094c1.781 0 3.15.156 4.11.469.958.312 1.77.87 2.437 1.671 1.062 1.271 1.594 3.063 1.594 5.375 0 1.844-.49 4.032-1.47 6.563L237.704 477h-12.687zm-7.547 4.625v-18.703h-10l1.547-4.625h12.5v23.328h-4.047zm-36.453-4.234l1.593-4.72c2.042-.228 3.688-.838 4.938-1.827 1.948-1.542 2.922-3.406 2.922-5.594 0-.906-.32-1.52-.961-1.844-.64-.323-1.873-.484-3.696-.484h-5.562v-9.625l4.047-1.36v6.36h4.062c2.5 0 4.243.36 5.227 1.078.984.719 1.476 1.984 1.476 3.797 0 3.437-1.317 6.49-3.953 9.156-2.635 2.667-6 4.354-10.093 5.063zm-5.391-6.657h-4.063c.73-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.954-.719-.479-1.963-.895-3.734-1.25l2.094-3.89c2.552.51 4.33 1.21 5.335 2.101 1.006.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM148.828 477l1.36-4.14h8.468c-3.833-3.709-6.713-8.162-8.64-13.36l4.093-1.578a28.973 28.973 0 004.938 8.875c1.469-1.781 2.203-3.943 2.203-6.485v-2.015h4.344v1.312c0 3.97-1.36 7.214-4.078 9.735 1.573 1.614 2.974 2.791 4.203 3.531l-1.39 4.125h-15.5zm-7.078 0v-9.594c0-1.927-.32-3.252-.96-3.976-.642-.724-1.998-1.295-4.071-1.711l2-3.797c2.448.448 4.208 1.15 5.281 2.11.708.635 1.185 1.385 1.43 2.25.244.864.367 2.25.367 4.156V477h-4.047zm-11.844 0v-9.063c0-2.416-.203-3.856-.61-4.32-.405-.463-1.681-.695-3.827-.695h-5.89l1.53-4.625h5.047c3.136 0 5.23.458 6.281 1.375a3.773 3.773 0 011.22 1.992c.197.797.296 2.18.296 4.149V477h-4.047zm207.406 31.734h-4.062c.73-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.954-.72-.479-1.964-.895-3.735-1.25l2.094-3.89c2.552.51 4.33 1.21 5.336 2.101 1.005.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zm-11.593 0h-4.063c.73-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.954-.719-.479-1.964-.895-3.735-1.25l2.094-3.89c2.552.51 4.331 1.21 5.336 2.101s1.508 2.206 1.508 3.946c0 1.094-.25 2.76-.75 5l-.406 1.765zM297.656 515l-2.203-17.5 3.922-1.578 1.172 9.25c1.135-.281 1.932-.945 2.39-1.992.459-1.047.688-2.716.688-5.008v-1.875h4.344v1.719c0 3.093-.565 5.445-1.696 7.054-1.13 1.61-2.919 2.607-5.367 2.993l.281 2.312c2.72 0 4.891-.453 6.516-1.36 2.844-1.583 4.266-5.192 4.266-10.827v-1.891h4.328v1.562c0 5.615-1.58 9.875-4.742 12.782-3.162 2.906-7.795 4.359-13.899 4.359zm-10.047 0v-9.594c0-1.927-.32-3.252-.96-3.976-.641-.724-1.998-1.295-4.07-1.711l2-3.797c2.447.448 4.207 1.15 5.28 2.11.709.635 1.185 1.385 1.43 2.25.245.864.367 2.25.367 4.156V515h-4.047zm-25.625 0l2.266-8.688c.417-1.593.625-2.692.625-3.296 0-1.24-1.057-2.907-3.172-5l3.61-2.094c1.145.958 2.124 2.265 2.937 3.922 1.667-2.615 3.667-3.922 6-3.922 1.76 0 3.167.67 4.219 2.008 1.052 1.338 1.677 3.257 1.875 5.757l.531 6.688-1.531 4.625h-9.031l1.546-4.625h4.922l-.36-4.797c-.25-3.354-1.348-5.031-3.296-5.031-1.125 0-2.07.56-2.836 1.68-.766 1.12-1.497 3.007-2.195 5.664L266.219 515h-4.235zm-3.953-6.266h-4.062c.729-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.079-1.954-.718-.479-1.963-.895-3.734-1.25l2.094-3.89c2.552.51 4.33 1.21 5.336 2.101 1.005.891 1.507 2.206 1.507 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM219.86 515v-.516c0-2.677.282-4.971.844-6.882.563-1.912 1.51-3.8 2.844-5.664l-.703-.672c-1.136-1.063-2.162-1.948-3.078-2.657l3.687-2.687c2.448 1.937 5.037 4.51 7.766 7.719.625-1.084 1.039-2.032 1.242-2.844.203-.813.305-1.927.305-3.344v-1.156h4.328v.906c0 3.521-1.183 6.568-3.547 9.14a41.618 41.618 0 014.422 6.47l-3.485 2.578c-2.062-3.709-4.864-7.355-8.406-10.938-1.26 1.958-1.89 4.948-1.89 8.969V515h-4.329zm-4.937-6.266h-4.063c.73-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.954-.719-.479-1.964-.895-3.734-1.25l2.093-3.89c2.552.51 4.331 1.21 5.336 2.101 1.006.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM192.75 515l1.563-4.625h5.515v-6.078c0-1.646-.354-2.724-1.062-3.235-.948-.677-2.224-1.244-3.828-1.703l1.843-3.437c1.73.427 3.297 1.047 4.703 1.86.907.53 1.532 1.182 1.875 1.952.344.771.516 1.922.516 3.454v7.187L202.328 515h-9.578zm-17.906 0l-2.235-17.688 3.922-1.39 1.797 14.453c2.667 0 4.794-.615 6.383-1.844 1.588-1.229 2.383-2.87 2.383-4.922 0-2.24-1.209-3.359-3.625-3.359-.646 0-1.339.104-2.078.313l1.406-4.36a9.102 9.102 0 012.234-.297c1.917 0 3.414.576 4.492 1.727 1.079 1.15 1.618 2.747 1.618 4.789 0 2.292-.698 4.437-2.094 6.437s-3.292 3.568-5.688 4.704c-2.01.958-4.849 1.437-8.515 1.437zm-9.938 0v-9.063c0-2.416-.203-3.856-.61-4.32-.405-.463-1.681-.695-3.827-.695h-5.89l1.53-4.625h5.047c3.136 0 5.23.458 6.281 1.375a3.773 3.773 0 011.22 1.992c.197.797.296 2.18.296 4.149V515h-4.047zm-24.625 0l1.563-4.625h5.515v-6.078c0-1.646-.354-2.724-1.062-3.235-.948-.677-2.224-1.244-3.828-1.703l1.844-3.437c1.729.427 3.296 1.047 4.703 1.86.906.53 1.53 1.182 1.875 1.952.343.771.515 1.922.515 3.454v7.187L149.86 515h-9.578zm-17.906 0l-2.234-17.688 3.921-1.39 1.797 14.453c2.667 0 4.795-.615 6.383-1.844 1.589-1.229 2.383-2.87 2.383-4.922 0-2.24-1.208-3.359-3.625-3.359-.646 0-1.339.104-2.078.313l1.406-4.36a9.102 9.102 0 012.234-.297c1.917 0 3.415.576 4.493 1.727 1.078 1.15 1.617 2.747 1.617 4.789 0 2.292-.698 4.437-2.094 6.437s-3.292 3.568-5.687 4.704c-2.01.958-4.85 1.437-8.516 1.437zm212.14 38l2.266-8.688c.417-1.593.625-2.692.625-3.296 0-1.24-1.057-2.907-3.172-5l3.61-2.094c1.146.958 2.125 2.265 2.937 3.922 1.667-2.615 3.667-3.922 6-3.922 1.76 0 3.167.67 4.219 2.008 1.052 1.338 1.677 3.257 1.875 5.758l.531 6.687-1.531 4.625h-9.031l1.547-4.625h4.921l-.359-4.797c-.25-3.354-1.349-5.031-3.297-5.031-1.125 0-2.07.56-2.836 1.68-.765 1.12-1.497 3.007-2.195 5.664L338.75 553h-4.234zm-23.28 0l1.546-4.625h4.031c-.822-2.115-1.234-4.708-1.234-7.781v-1.672h-2.328l1.547-4.625h8.578c3.135 0 5.23.458 6.281 1.375a3.733 3.733 0 011.235 1.984c.198.792.296 2.177.296 4.157V553h-4.046v-9.063c0-2.416-.203-3.856-.61-4.32-.406-.463-1.682-.695-3.828-.695h-3.062v1.375c0 3.354.479 6.047 1.437 8.078L319.547 553h-8.313zm-7.173 0v-9.594c0-1.927-.32-3.252-.96-3.976-.641-.724-1.998-1.295-4.07-1.711l2-3.797c2.447.448 4.208 1.15 5.28 2.11.709.635 1.185 1.385 1.43 2.25.245.864.367 2.25.367 4.155V553h-4.046zm-25.515 0v-14.078h-2.531l1.562-4.625h10.89c3.136 0 5.23.458 6.282 1.375a3.734 3.734 0 011.234 1.992c.198.797.297 2.18.297 4.149V553h-4.047v-9.063c0-2.416-.203-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-5.203V553h-4.047zm-20.485 0l1.532-4.625h8.734c.865-1.99 1.297-3.682 1.297-5.078 0-1.646-.38-2.787-1.14-3.422-.761-.635-2.13-.953-4.11-.953h-5.406l1.531-4.625h5.094c1.781 0 3.15.156 4.11.469.957.312 1.77.87 2.437 1.672 1.062 1.27 1.593 3.062 1.593 5.375 0 1.843-.49 4.03-1.468 6.562L270.75 553h-12.688zm-21.156 0l2.266-8.688c.417-1.593.625-2.692.625-3.296 0-1.24-1.057-2.907-3.172-5l3.61-2.094c1.145.958 2.124 2.265 2.937 3.922 1.667-2.615 3.667-3.922 6-3.922 1.76 0 3.167.67 4.219 2.008 1.052 1.338 1.677 3.257 1.875 5.758l.53 6.687-1.53 4.625h-9.032l1.547-4.625h4.922l-.36-4.797c-.25-3.354-1.348-5.031-3.296-5.031-1.125 0-2.07.56-2.836 1.68-.766 1.12-1.497 3.007-2.195 5.664L241.14 553h-4.235zm-3.953-6.266h-4.062c.729-2.666 1.093-4.573 1.093-5.718 0-.823-.359-1.474-1.078-1.953-.719-.48-1.963-.896-3.734-1.25l2.094-3.891c2.552.51 4.33 1.21 5.336 2.101 1.005.891 1.507 2.206 1.507 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM204.641 553v-14.078h-2.485l1.547-4.625h10.484c3.136 0 5.23.458 6.282 1.375a3.734 3.734 0 011.234 1.992c.198.797.297 2.18.297 4.149v6.562L220.453 553h-15.812zm4.062-4.625h9.25v-4.438c0-2.427-.203-3.87-.61-4.328-.406-.458-1.682-.687-3.827-.687h-4.813v9.453zm-20.953-1.64h-4.063c.73-2.667 1.094-4.574 1.094-5.72 0-.822-.36-1.473-1.078-1.952-.719-.48-1.963-.896-3.734-1.25l2.094-3.891c2.552.51 4.33 1.21 5.335 2.101 1.006.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM172.656 553v-9.594c0-1.927-.32-3.252-.96-3.976-.641-.724-1.998-1.295-4.071-1.711l2-3.797c2.448.448 4.208 1.15 5.281 2.11.709.635 1.185 1.385 1.43 2.25.245.864.367 2.25.367 4.155V553h-4.047zm-27.75 0l1.547-4.625h4.031c-.823-2.115-1.234-4.708-1.234-7.781v-1.672h-2.328l1.547-4.625h8.578c3.135 0 5.229.458 6.281 1.375a3.733 3.733 0 011.234 1.984c.198.792.297 2.177.297 4.157V553h-4.047v-9.063c0-2.416-.203-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-3.063v1.375c0 3.354.48 6.047 1.438 8.078L153.219 553h-8.313zm-7.094 0v-9.063c0-2.416-.203-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-5.89l1.53-4.625h5.048c3.135 0 5.229.458 6.28 1.375a3.773 3.773 0 011.22 1.992c.197.797.296 2.18.296 4.149V553h-4.047zm-17.359 0v-5.016h5.016V553h-5.016zm322.688 38.39l1.593-4.718c2.042-.23 3.688-.839 4.938-1.828 1.948-1.542 2.922-3.406 2.922-5.594 0-.906-.32-1.52-.961-1.844-.64-.323-1.873-.484-3.695-.484h-5.563v-9.625l4.047-1.36v6.36h4.062c2.5 0 4.243.36 5.227 1.078.984.719 1.476 1.984 1.476 3.797 0 3.437-1.317 6.49-3.953 9.156-2.635 2.667-6 4.354-10.093 5.063zm-22.594-.39l2.265-8.688c.417-1.593.625-2.692.625-3.296 0-1.24-1.057-2.907-3.171-5l3.609-2.094c1.146.958 2.125 2.265 2.938 3.922 1.666-2.615 3.666-3.922 6-3.922 1.76 0 3.166.67 4.218 2.008s1.677 3.257 1.875 5.758l.531 6.687-1.53 4.625h-9.032l1.547-4.625h4.922l-.36-4.797c-.25-3.354-1.349-5.031-3.296-5.031-1.126 0-2.07.56-2.836 1.68-.766 1.12-1.498 3.007-2.196 5.664L424.781 591h-4.234zm-21.625 2.781l1.562-4.672c1.667 0 3.459-.28 5.375-.843L402.5 573.28l3.875-1.36 3.266 14.563c1.635-1.24 2.773-2.78 3.414-4.625.64-1.843.96-4.484.96-7.922v-1.64h4.344v1.156c0 3.99-.612 7.318-1.836 9.985-1.224 2.666-3.153 4.88-5.789 6.64-3.698 2.469-7.635 3.703-11.812 3.703zM380.078 591l-2.203-17.5 3.922-1.578 1.172 9.25c1.135-.281 1.932-.945 2.39-1.992.459-1.047.688-2.716.688-5.008v-1.875h4.344v1.719c0 3.093-.565 5.445-1.696 7.054-1.13 1.61-2.919 2.607-5.367 2.992l.281 2.313c2.72 0 4.891-.453 6.516-1.36 2.844-1.583 4.266-5.192 4.266-10.827v-1.891h4.328v1.562c0 5.615-1.581 9.875-4.742 12.782-3.162 2.906-7.795 4.359-13.899 4.359zm-23.562 0v-10.031h4.046V591h-4.046zm13.593 0v-9.063c0-2.416-.205-3.856-.617-4.32-.411-.463-1.685-.695-3.82-.695h-12.047l1.547-4.625h11.172c3.135 0 5.229.458 6.281 1.375a3.733 3.733 0 011.234 1.984c.198.792.297 2.177.297 4.157V591h-4.047zm-21.78 0h-1.923v-5.016h5.016v3.891c0 4.094-1.672 6.14-5.016 6.14v-1.734c1.282 0 1.922-.927 1.922-2.781v-.5zm-31.204 0l1.531-4.625h8.735c.864-1.99 1.296-3.682 1.296-5.078 0-1.646-.38-2.787-1.14-3.422-.76-.635-2.13-.953-4.11-.953h-5.406l1.531-4.625h5.094c1.782 0 3.151.156 4.11.469.958.312 1.77.87 2.437 1.672 1.063 1.27 1.594 3.062 1.594 5.375 0 1.843-.49 4.03-1.469 6.562L329.813 591h-12.688zm-3.953-6.266h-4.063c.73-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.953-.719-.48-1.964-.896-3.734-1.25l2.093-3.891c2.552.51 4.331 1.21 5.336 2.101 1.006.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM298.078 591v-9.594c0-1.927-.32-3.252-.96-3.976-.641-.724-1.998-1.295-4.071-1.711l2-3.797c2.448.448 4.208 1.15 5.281 2.11.708.635 1.185 1.385 1.43 2.25.245.864.367 2.25.367 4.155V591h-4.047zm-25.14 0v-14.078h-2.485l1.547-4.625h10.484c3.136 0 5.23.458 6.282 1.375a3.734 3.734 0 011.234 1.992c.198.797.297 2.18.297 4.149v6.562L288.75 591h-15.813zm4.062-4.625h9.25v-4.438c0-2.427-.203-3.87-.61-4.328-.406-.458-1.682-.687-3.827-.687H277v9.453zM265.203 591h-1.922v-5.016h5.016v3.891c0 4.094-1.672 6.14-5.016 6.14v-1.734c1.282 0 1.922-.927 1.922-2.781v-.5zm-20.484 0v-9.063c0-2.416-.203-3.856-.61-4.32-.406-.463-1.682-.695-3.828-.695h-5.89l1.53-4.625h5.048c3.135 0 5.229.458 6.281 1.375a3.773 3.773 0 011.219 1.992c.198.797.297 2.18.297 4.149V591h-4.047zm-17.547 0v-9.594c0-1.927-.32-3.252-.961-3.976-.64-.724-1.997-1.295-4.07-1.711l2-3.797c2.448.448 4.208 1.15 5.28 2.11.71.635 1.186 1.385 1.43 2.25.245.864.368 2.25.368 4.155V591h-4.047zm-23.719 0l1.547-4.625h9.64v-4.438c0-2.416-.202-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-5.828l1.547-4.625h4.953c3.135 0 5.23.458 6.281 1.375a3.734 3.734 0 011.235 1.992c.198.797.297 2.18.297 4.149v6.562h2.5L219.655 591h-16.203zm-30.594 0v-.516c0-2.677.282-4.971.844-6.882.563-1.912 1.51-3.8 2.844-5.664l-.703-.672c-1.136-1.063-2.162-1.948-3.078-2.657l3.687-2.687c2.448 1.937 5.037 4.51 7.766 7.719.625-1.084 1.039-2.032 1.242-2.844.203-.813.305-1.927.305-3.344v-1.156h4.328v.906c0 3.521-1.183 6.568-3.547 9.14a41.618 41.618 0 014.422 6.47l-3.485 2.578c-2.062-3.709-4.864-7.355-8.406-10.938-1.26 1.958-1.89 4.948-1.89 8.969V591h-4.329zm-24.265 0l1.547-4.625h4.03c-.822-2.115-1.233-4.708-1.233-7.781v-1.672h-2.329l1.547-4.625h8.578c3.136 0 5.23.458 6.282 1.375a3.733 3.733 0 011.234 1.984c.198.792.297 2.177.297 4.157V591H164.5v-9.063c0-2.416-.203-3.856-.61-4.32-.406-.463-1.682-.695-3.827-.695H157v1.375c0 3.354.48 6.047 1.438 8.078L156.905 591h-8.312zm-7.094 0v-9.063c0-2.416-.203-3.856-.61-4.32-.406-.463-1.682-.695-3.827-.695h-5.891l1.531-4.625h5.047c3.135 0 5.23.458 6.281 1.375a3.773 3.773 0 011.219 1.992c.198.797.297 2.18.297 4.149V591H141.5zm-14.047-6.266h-4.062c.729-2.666 1.093-4.573 1.093-5.718 0-.823-.359-1.474-1.078-1.953-.719-.48-1.963-.896-3.734-1.25l2.094-3.891c2.552.51 4.33 1.21 5.336 2.101 1.005.891 1.507 2.206 1.507 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM406.016 629v-10.031h4.046V629h-4.046zm13.593 0v-9.063c0-2.416-.205-3.856-.617-4.32-.411-.463-1.685-.695-3.82-.695h-12.047l1.547-4.625h11.172c3.135 0 5.229.458 6.281 1.375a3.733 3.733 0 011.234 1.984c.198.792.297 2.177.297 4.157V629h-4.047zm-36.421 0v-.516c0-2.677.28-4.971.843-6.882.563-1.912 1.51-3.8 2.844-5.664l-.703-.672c-1.136-1.063-2.162-1.948-3.078-2.657l3.687-2.687c2.448 1.937 5.037 4.51 7.766 7.719.625-1.084 1.039-2.032 1.242-2.844.203-.813.305-1.927.305-3.344v-1.156h4.328v.906c0 3.521-1.182 6.568-3.547 9.14a41.618 41.618 0 014.422 6.47l-3.485 2.578c-2.062-3.709-4.864-7.355-8.406-10.938-1.26 1.958-1.89 4.948-1.89 8.969V629h-4.329zm-4.938-6.266h-4.063c.73-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.953-.719-.48-1.963-.896-3.734-1.25l2.094-3.891c2.552.51 4.33 1.21 5.335 2.101 1.006.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM356.078 629l1.563-4.625h5.515v-6.078c0-1.646-.354-2.724-1.062-3.235-.948-.677-2.224-1.244-3.828-1.703l1.843-3.437c1.73.427 3.297 1.047 4.704 1.86.906.53 1.53 1.182 1.875 1.952.343.771.515 1.922.515 3.453v7.188L365.656 629h-9.578zm-17.906 0l-2.235-17.688 3.922-1.39 1.797 14.453c2.667 0 4.795-.615 6.383-1.844 1.589-1.229 2.383-2.87 2.383-4.922 0-2.24-1.208-3.359-3.625-3.359-.646 0-1.339.104-2.078.313l1.406-4.36a9.102 9.102 0 012.234-.297c1.917 0 3.414.576 4.493 1.727 1.078 1.15 1.617 2.747 1.617 4.789 0 2.292-.698 4.437-2.094 6.437s-3.292 3.568-5.688 4.703c-2.01.959-4.848 1.438-8.515 1.438zm-9.938 0v-9.063c0-2.416-.203-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-5.89l1.53-4.625h5.047c3.136 0 5.23.458 6.282 1.375a3.773 3.773 0 011.218 1.992c.198.797.297 2.18.297 4.149V629h-4.047zm-24.625 0l1.563-4.625h5.515v-6.078c0-1.646-.354-2.724-1.062-3.235-.948-.677-2.224-1.244-3.828-1.703l1.844-3.437c1.729.427 3.296 1.047 4.703 1.86.906.53 1.531 1.182 1.875 1.952.344.771.515 1.922.515 3.453v7.188L313.188 629h-9.579zm-17.906 0l-2.234-17.688 3.922-1.39 1.796 14.453c2.667 0 4.795-.615 6.383-1.844 1.589-1.229 2.383-2.87 2.383-4.922 0-2.24-1.208-3.359-3.625-3.359-.646 0-1.338.104-2.078.313l1.406-4.36a9.102 9.102 0 012.235-.297c1.916 0 3.414.576 4.492 1.727 1.078 1.15 1.617 2.747 1.617 4.789 0 2.292-.698 4.437-2.094 6.437s-3.291 3.568-5.687 4.703c-2.01.959-4.85 1.438-8.516 1.438zm-34.078 0v-10.031h4.047V629h-4.047zm13.594 0v-9.063c0-2.416-.206-3.856-.617-4.32-.412-.463-1.685-.695-3.82-.695h-12.048l1.547-4.625h11.172c3.136 0 5.23.458 6.281 1.375a3.733 3.733 0 011.235 1.984c.198.792.297 2.177.297 4.157V629h-4.047zm-37.594 0l2.266-8.688c.416-1.593.625-2.692.625-3.296 0-1.24-1.058-2.907-3.172-5l3.61-2.094c1.145.958 2.124 2.265 2.937 3.922 1.666-2.615 3.666-3.922 6-3.922 1.76 0 3.166.67 4.218 2.008s1.677 3.257 1.875 5.758l.532 6.687-1.532 4.625h-9.03l1.546-4.625h4.922l-.36-4.797c-.25-3.354-1.348-5.031-3.296-5.031-1.125 0-2.07.56-2.836 1.68-.766 1.12-1.498 3.007-2.196 5.664L231.86 629h-4.234zm-7.453 0v-9.594c0-1.927-.32-3.252-.961-3.976-.64-.724-1.997-1.295-4.07-1.711l2-3.797c2.448.448 4.208 1.15 5.28 2.11.71.635 1.186 1.385 1.43 2.25.245.864.368 2.25.368 4.155V629h-4.047zm-13.531 0v-14.078h-9.532l1.547-4.625h16.094l-1.547 4.625h-2.516V629h-4.046zm-16.282 0v-9.063c0-2.416-.203-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-5.89l1.53-4.625h5.047c3.136 0 5.23.458 6.282 1.375a3.773 3.773 0 011.218 1.992c.198.797.297 2.18.297 4.149V629h-4.047zm-24.625 0l1.563-4.625h5.516v-6.078c0-1.646-.355-2.724-1.063-3.235-.948-.677-2.224-1.244-3.828-1.703l1.844-3.437c1.729.427 3.296 1.047 4.703 1.86.906.53 1.531 1.182 1.875 1.952.344.771.515 1.922.515 3.453v7.188L175.312 629h-9.578zm-3.625-6.266h-4.062c.729-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.953-.72-.48-1.964-.896-3.735-1.25l2.094-3.891c2.552.51 4.33 1.21 5.336 2.101 1.005.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.407 1.765zm-11.593 0h-4.063c.73-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.953-.719-.48-1.964-.896-3.735-1.25l2.094-3.891c2.552.51 4.33 1.21 5.336 2.101 1.005.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM122.203 629v-14.078h-2.484l1.547-4.625h10.484c3.135 0 5.23.458 6.281 1.375a3.734 3.734 0 011.235 1.992c.198.797.297 2.18.297 4.149v6.562L138.016 629h-15.813zm4.063-4.625h9.25v-4.438c0-2.427-.203-3.87-.61-4.328-.406-.458-1.682-.687-3.828-.687h-4.812v9.453zM401.844 667l2.265-8.688c.417-1.593.625-2.692.625-3.296 0-1.24-1.057-2.907-3.171-5l3.609-2.094c1.146.958 2.125 2.265 2.937 3.922 1.667-2.615 3.667-3.922 6-3.922 1.76 0 3.167.67 4.22 2.008 1.051 1.338 1.676 3.257 1.874 5.758l.531 6.687-1.53 4.625h-9.032l1.547-4.625h4.922l-.36-4.797c-.25-3.354-1.349-5.031-3.297-5.031-1.125 0-2.07.56-2.836 1.68-.765 1.12-1.497 3.007-2.195 5.664L406.078 667h-4.234zm-20.422 0l-2.203-17.5 3.922-1.578 1.171 9.25c1.136-.281 1.933-.945 2.391-1.992.458-1.047.688-2.716.688-5.008v-1.875h4.343v1.719c0 3.093-.565 5.445-1.695 7.054-1.13 1.61-2.92 2.607-5.367 2.992l.281 2.313c2.719 0 4.89-.453 6.516-1.36 2.844-1.583 4.265-5.192 4.265-10.827v-1.891h4.329v1.562c0 5.615-1.581 9.875-4.743 12.782-3.161 2.906-7.794 4.359-13.898 4.359zm-18.969.39l1.594-4.718c2.042-.23 3.687-.839 4.937-1.828 1.948-1.542 2.922-3.406 2.922-5.594 0-.906-.32-1.52-.96-1.844-.641-.323-1.873-.484-3.696-.484h-5.563v-9.625l4.047-1.36v6.36h4.063c2.5 0 4.242.36 5.226 1.078.985.719 1.477 1.984 1.477 3.797 0 3.437-1.318 6.49-3.953 9.156-2.636 2.667-6 4.354-10.094 5.063zm-20.687-.39l1.546-4.625h9.641v-4.438c0-2.416-.203-3.856-.61-4.32-.406-.463-1.682-.695-3.827-.695h-5.829l1.547-4.625h4.954c3.135 0 5.229.458 6.28 1.375a3.734 3.734 0 011.235 1.992c.198.797.297 2.18.297 4.149v6.562h2.5L357.969 667h-16.203zm-4.016-6.266h-4.063c.73-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.953-.719-.48-1.963-.896-3.734-1.25l2.094-3.891c2.552.51 4.33 1.21 5.335 2.101 1.006.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM309.437 667v-14.078h-2.484l1.547-4.625h10.484c3.136 0 5.23.458 6.282 1.375a3.734 3.734 0 011.234 1.992c.198.797.297 2.18.297 4.149v6.562L325.25 667h-15.813zm4.063-4.625h9.25v-4.438c0-2.427-.203-3.87-.61-4.328-.406-.458-1.682-.687-3.827-.687H313.5v9.453zM276.078 667l-2.203-17.5 3.922-1.578 1.172 9.25c1.135-.281 1.932-.945 2.39-1.992.459-1.047.688-2.716.688-5.008v-1.875h4.344v1.719c0 3.093-.565 5.445-1.696 7.054-1.13 1.61-2.919 2.607-5.367 2.992l.281 2.313c2.72 0 4.891-.453 6.516-1.36 2.844-1.583 4.266-5.192 4.266-10.827v-1.891h4.328v1.562c0 5.615-1.581 9.875-4.742 12.782-3.162 2.906-7.795 4.359-13.899 4.359zm-21.937 0l1.39-4.234H266v-4.157c0-1.948-.193-3.205-.578-3.773-.386-.568-1.386-1.065-3-1.492l-2.985-.782-.234.672c-.135.396-.203.74-.203 1.032 0 1.01.87 1.583 2.61 1.718l-1.079 3.25c-3.5-.323-5.25-1.671-5.25-4.047 0-.916.375-2.437 1.125-4.562l.969-2.703 7.125 1.734c2.313.563 3.815 1.284 4.508 2.164.693.88 1.039 2.503 1.039 4.867v6.079L268.64 667h-14.5zm-20.657 0v-10.031h4.047V667h-4.047zm13.594 0v-9.063c0-2.416-.206-3.856-.617-4.32-.412-.463-1.685-.695-3.82-.695h-12.047l1.547-4.625h11.172c3.135 0 5.229.458 6.28 1.375a3.733 3.733 0 011.235 1.984c.198.792.297 2.177.297 4.157V667h-4.047zm-38.234 0v-7.016c0-2.354.682-4.343 2.047-5.968L206 651.938l1.719-4.016 11.094 4.719-1.672 4.062-3.125-1.36c-.75.938-1.125 2.256-1.125 3.954V667h-4.047zm-9.735 0v-9.594c0-1.927-.32-3.252-.96-3.976-.641-.724-1.998-1.295-4.07-1.711l2-3.797c2.447.448 4.207 1.15 5.28 2.11.709.635 1.185 1.385 1.43 2.25.245.864.367 2.25.367 4.155V667h-4.047zm-11.734 0v-5.016h5.016V667h-5.016zm-32.156 0v-10.031h4.047V667h-4.047zm13.594 0v-9.063c0-2.416-.206-3.856-.618-4.32-.411-.463-1.685-.695-3.82-.695h-12.047l1.547-4.625h11.172c3.135 0 5.229.458 6.281 1.375a3.733 3.733 0 011.234 1.984c.198.792.297 2.177.297 4.157V667h-4.047zm-20.391-6.266h-4.063c.73-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.078-1.953-.719-.48-1.964-.896-3.734-1.25l2.093-3.891c2.552.51 4.331 1.21 5.336 2.101 1.006.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.406 1.765zM120.797 667v-.516c0-2.677.281-4.971.844-6.882.562-1.912 1.51-3.8 2.843-5.664l-.703-.672c-1.135-1.063-2.161-1.948-3.078-2.657l3.688-2.687c2.448 1.937 5.036 4.51 7.765 7.719.625-1.084 1.04-2.032 1.242-2.844.204-.813.305-1.927.305-3.344v-1.156h4.328v.906c0 3.521-1.182 6.568-3.547 9.14a41.618 41.618 0 014.422 6.47l-3.484 2.578c-2.063-3.709-4.865-7.355-8.406-10.938-1.26 1.958-1.891 4.948-1.891 8.969V667h-4.328zm275.937 31.734h-4.062c.729-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.079-1.953-.718-.48-1.963-.896-3.734-1.25l2.094-3.891c2.552.51 4.33 1.21 5.336 2.101 1.005.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.407 1.765zM380.031 705v-14.078H370.5l1.547-4.625h16.094l-1.547 4.625h-2.516V705h-4.047zm-16.36 0v-9.594c0-1.927-.32-3.252-.96-3.976-.64-.724-1.997-1.295-4.07-1.711l2-3.797c2.448.448 4.208 1.15 5.28 2.11.71.635 1.186 1.385 1.43 2.25.245.864.368 2.25.368 4.155V705h-4.047zm-26.093 2.781l1.563-4.672c1.666 0 3.458-.28 5.375-.843l-3.36-14.985 3.875-1.36 3.266 14.563c1.635-1.24 2.773-2.78 3.414-4.625.64-1.843.96-4.484.96-7.922v-1.64h4.345v1.156c0 3.99-.612 7.318-1.836 9.985-1.224 2.666-3.154 4.88-5.79 6.64-3.697 2.469-7.635 3.703-11.812 3.703zM318.188 705v-10.031h4.046V705h-4.046zm13.593 0v-9.063c0-2.416-.205-3.856-.617-4.32-.411-.463-1.685-.695-3.82-.695h-12.047l1.547-4.625h11.172c3.135 0 5.229.458 6.28 1.375a3.733 3.733 0 011.235 1.984c.198.792.297 2.177.297 4.157V705h-4.047zm-46.234 0l1.547-4.625h9.64v-4.438c0-2.416-.203-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-5.828l1.547-4.625h4.953c3.135 0 5.229.458 6.281 1.375a3.734 3.734 0 011.234 1.992c.198.797.297 2.18.297 4.149v6.562h2.5L301.75 705h-16.203zm-21.688 2.781l1.563-4.672c1.667 0 3.458-.28 5.375-.843l-3.36-14.985 3.875-1.36 3.266 14.563c1.636-1.24 2.774-2.78 3.414-4.625.64-1.843.961-4.484.961-7.922v-1.64h4.344v1.156c0 3.99-.612 7.318-1.836 9.985-1.224 2.666-3.154 4.88-5.79 6.64-3.697 2.469-7.635 3.703-11.812 3.703zm-2.375-9.047h-4.062c.729-2.666 1.094-4.573 1.094-5.718 0-.823-.36-1.474-1.079-1.953-.718-.48-1.963-.896-3.734-1.25l2.094-3.891c2.552.51 4.33 1.21 5.336 2.101 1.005.891 1.508 2.206 1.508 3.946 0 1.094-.25 2.76-.75 5l-.407 1.765zm-27.328 10.891v-14.61h4.047v14.61h-4.047zm-2.515-18.703l1.547-4.625h10.843c1.99 0 3.39.088 4.203.265.813.178 1.495.542 2.047 1.094.802.813 1.203 1.98 1.203 3.5 0 3.052-1.12 5.901-3.359 8.547-2.24 2.646-5.13 4.537-8.672 5.672l1.672-4.969c1.656-.469 3.036-1.383 4.14-2.742 1.105-1.36 1.657-2.82 1.657-4.383 0-.937-.328-1.565-.984-1.883-.657-.317-1.959-.476-3.907-.476h-10.39zM224.406 705v-9.063c0-2.416-.203-3.856-.61-4.32-.405-.463-1.681-.695-3.827-.695h-5.89l1.53-4.625h5.047c3.136 0 5.23.458 6.281 1.375a3.773 3.773 0 011.22 1.992c.197.797.296 2.18.296 4.149V705h-4.047zm-38.734 0l1.531-4.625h8.734c.865-1.99 1.297-3.682 1.297-5.078 0-1.646-.38-2.787-1.14-3.422-.76-.635-2.13-.953-4.11-.953h-5.406l1.531-4.625h5.094c1.781 0 3.151.156 4.11.469.958.312 1.77.87 2.437 1.672 1.063 1.27 1.594 3.062 1.594 5.375 0 1.843-.49 4.03-1.469 6.562L198.359 705h-12.687zm-20.422 0l-2.203-17.5 3.922-1.578 1.172 9.25c1.135-.281 1.932-.945 2.39-1.992.459-1.047.688-2.716.688-5.008v-1.875h4.344v1.719c0 3.093-.566 5.445-1.696 7.054-1.13 1.61-2.92 2.607-5.367 2.992l.281 2.313c2.719 0 4.89-.453 6.516-1.36 2.844-1.583 4.266-5.192 4.266-10.827v-1.891h4.328v1.562c0 5.615-1.581 9.875-4.743 12.782-3.161 2.906-7.794 4.359-13.898 4.359zm-21.938 0l1.391-4.234h10.469v-4.157c0-1.948-.193-3.205-.578-3.773-.386-.568-1.386-1.065-3-1.492l-2.985-.782-.234.672c-.135.396-.203.74-.203 1.032 0 1.01.87 1.583 2.61 1.718l-1.079 3.25c-3.5-.323-5.25-1.671-5.25-4.047 0-.916.375-2.437 1.125-4.562l.969-2.703 7.125 1.734c2.312.563 3.815 1.284 4.508 2.164.692.88 1.039 2.503 1.039 4.867v6.079L157.813 705h-14.5zm-20.656 0v-10.031h4.047V705h-4.047zm13.594 0v-9.063c0-2.416-.206-3.856-.617-4.32-.412-.463-1.685-.695-3.82-.695h-12.047l1.546-4.625h11.172c3.136 0 5.23.458 6.282 1.375a3.733 3.733 0 011.234 1.984c.198.792.297 2.177.297 4.157V705h-4.047zm169.5 38v-10.031h4.047V743h-4.047zm13.594 0v-9.063c0-2.416-.206-3.856-.617-4.32-.412-.463-1.685-.695-3.82-.695h-12.048l1.547-4.625h11.172c3.136 0 5.23.458 6.281 1.375a3.733 3.733 0 011.235 1.984c.198.792.297 2.177.297 4.157V743h-4.047zm-37.594 0l2.266-8.688c.416-1.593.625-2.692.625-3.296 0-1.24-1.058-2.907-3.172-5l3.61-2.094c1.145.958 2.124 2.265 2.937 3.922 1.666-2.615 3.666-3.922 6-3.922 1.76 0 3.166.67 4.218 2.008s1.677 3.257 1.875 5.758l.532 6.687-1.532 4.625h-9.03l1.546-4.625h4.922l-.36-4.797c-.25-3.354-1.348-5.031-3.296-5.031-1.125 0-2.07.56-2.836 1.68-.766 1.12-1.498 3.007-2.196 5.664L285.984 743h-4.234zm-7.453 0v-9.594c0-1.927-.32-3.252-.961-3.976-.64-.724-1.997-1.295-4.07-1.711l2-3.797c2.448.448 4.208 1.15 5.28 2.11.71.635 1.186 1.385 1.43 2.25.245.864.368 2.25.368 4.155V743h-4.047zm-22.703 0l-2.235-17.688 3.922-1.39 1.797 14.453c2.667 0 4.794-.615 6.383-1.844 1.588-1.229 2.383-2.87 2.383-4.922 0-2.24-1.209-3.359-3.625-3.359-.646 0-1.339.104-2.078.313l1.406-4.36a9.102 9.102 0 012.234-.297c1.917 0 3.414.576 4.492 1.727 1.079 1.15 1.618 2.747 1.618 4.789 0 2.292-.698 4.437-2.094 6.437s-3.292 3.568-5.688 4.703c-2.01.959-4.849 1.438-8.515 1.438zm-21.813 0l1.547-4.625h9.64v-4.438c0-2.416-.202-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-5.828l1.547-4.625h4.953c3.136 0 5.23.458 6.281 1.375a3.734 3.734 0 011.235 1.992c.198.797.297 2.18.297 4.149v6.562h2.5L245.984 743h-16.203zm-21.687 2.781l1.562-4.672c1.667 0 3.459-.28 5.375-.843l-3.36-14.985 3.876-1.36 3.266 14.563c1.635-1.24 2.773-2.78 3.414-4.625.64-1.843.96-4.484.96-7.922v-1.64h4.344v1.156c0 3.99-.612 7.318-1.836 9.985-1.224 2.666-3.153 4.88-5.789 6.64-3.698 2.469-7.635 3.703-11.812 3.703zM186.39 743l1.547-4.625h4.03c-.822-2.115-1.234-4.708-1.234-7.781v-1.672h-2.328l1.547-4.625h8.578c3.136 0 5.23.458 6.281 1.375a3.733 3.733 0 011.235 1.984c.198.792.297 2.177.297 4.157V743h-4.047v-9.063c0-2.416-.203-3.856-.61-4.32-.406-.463-1.682-.695-3.828-.695h-3.062v1.375c0 3.354.479 6.047 1.437 8.078l-1.53 4.625h-8.313zm-29.516 0l1.547-4.625h9.64v-4.438c0-2.416-.203-3.856-.609-4.32-.406-.463-1.682-.695-3.828-.695h-5.828l1.547-4.625h4.953c3.135 0 5.229.458 6.281 1.375a3.734 3.734 0 011.234 1.992c.198.797.297 2.18.297 4.149v6.562h2.5L173.08 743h-16.204zm-9.125 0v-14.078h-9.531l1.547-4.625h16.093l-1.547 4.625h-2.515V743h-4.047zm-28.25 0l1.39-4.234h10.47v-4.157c0-1.948-.193-3.205-.579-3.773-.385-.568-1.385-1.065-3-1.492l-2.984-.782-.234.672c-.136.396-.204.74-.204 1.032 0 1.01.87 1.583 2.61 1.718l-1.078 3.25c-3.5-.323-5.25-1.671-5.25-4.047 0-.916.375-2.437 1.125-4.562l.968-2.703 7.125 1.734c2.313.563 3.815 1.284 4.508 2.164.693.88 1.04 2.503 1.04 4.867v6.079L134 743h-14.5z"/><path id="Rectangle-1" stroke="#C06334" stroke-width="2" d="M117 106h282v198H117z"/><g id="Rectangle-209-+-Rectangle-208" fill="#DBAF88" transform="translate(15 14)"><path id="Rectangle-209" d="M0 1h50v300H0z"/><path id="Rectangle-208" d="M165.5-129.5h50v311h-50z" transform="rotate(-90 190.5 26)"/></g><path id="Rectangle-5" fill="url(#linearGradient-1)" d="M45-7h275v447H45z" transform="rotate(-180 182.5 216.5)"/><path id="Rectangle-6" fill="#FFF" d="M319 8h162v399H319z"/><g id="Group-2" transform="translate(65 65)"><rect id="Rectangle-19" width="20" height="239" x=".5" y=".5" fill="#D1CFCD" stroke="#D1CFCD" rx="3"/><g id="Rectangle-18-+-Triangle-1"><rect id="Rectangle-18" width="20" height="19" x=".5" y=".5" fill="url(#linearGradient-2)" stroke="#D1CFCD" rx="3"/><path id="Triangle-1" fill="#7E7C7B" d="M10.5 7l4.2 6H6.3z"/></g><g id="Rectangle-18-+-Triangle-2" transform="matrix(1 0 0 -1 0 240)"><rect id="Rectangle-18" width="20" height="19" x=".5" y=".5" fill="url(#linearGradient-2)" stroke="#D1CFCD" rx="3"/><path id="Triangle-1" fill="#7E7C7B" d="M10.5 7l4.2 6H6.3z"/></g><g id="Rectangle-18-+-Triangle-3-+-Group" transform="translate(0 50)"><g id="Rectangle-18-+-Triangle-3" fill="url(#linearGradient-3)" stroke="#D1CFCD" transform="matrix(1 0 0 -1 0 51)"><rect id="Rectangle-18" width="20" height="50" x=".5" y=".5" rx="3"/></g><g id="Group" fill="#D1CFCD" stroke="#7E7C7B" transform="translate(5.25 20)"><path id="Rectangle-22" d="M.5.5H10v1H.5z"/><path id="Rectangle-23" d="M.5 3.5H10v1H.5z"/><path id="Rectangle-24" d="M.5 6.5H10v1H.5z"/><path id="Rectangle-25" d="M.5 9.5H10v1H.5z"/></g></g></g><path id="Line-7" fill="#C06334" fill-rule="nonzero" d="M65.41 15.68l7 14h-6.001v20h6.001l-7 14-7-14h5.999v-20H58.41l7-14z"/><path id="Line-28" fill="#C06334" fill-rule="nonzero" d="M72 57.68l14 7-14 7-.001-6h-42.59l.001 6-14-7 14-7-.001 6h42.59l.001-6z"/><text id="clientTop:25px-=-bor" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="78.7" y="43" fill="#C06334">clientTop:</tspan> <tspan x="150.7" y="43" fill="#1C85B5">25px </tspan> <tspan x="186.7" y="43" fill="#C06334">= border</tspan></text><text id="clientLeft:41px" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal" transform="rotate(-90 40.32 125)"><tspan x="-13.68" y="129" fill="#C06334">clientLeft:</tspan> <tspan x="65.52" y="129" fill="#1C85B5">41px</tspan></text><g id="Rectangle-8-+-Rectangle-7" transform="translate(-42 -3)"><path id="Rectangle-8" fill="url(#linearGradient-1)" d="M86.5-85.5h275v447h-275z" transform="rotate(-90 224 138)"/><path id="Rectangle-7" fill="#FFF" d="M152 156h162v399H152z" transform="rotate(90 233 355.5)"/></g></g></g></svg> \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl@2x.png b/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl@2x.png deleted file mode 100644 index 48510e6e09..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-left-top-rtl@2x.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-left-top.png b/2-ui/1-document/09-size-and-scroll/metric-client-left-top.png deleted file mode 100644 index 33faf9be7d..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-left-top.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-left-top.svg b/2-ui/1-document/09-size-and-scroll/metric-client-left-top.svg new file mode 100644 index 0000000000..968590466e --- /dev/null +++ b/2-ui/1-document/09-size-and-scroll/metric-client-left-top.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="353" height="316" viewBox="0 0 353 316"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><linearGradient id="linearGradient-1" x1="0%" x2="62.299%" y1="47.096%" y2="47.096%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#FFF"/></linearGradient></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="metric-client-left-top.svg"><path fill="#FFF" d="M0 0h353v316H0z"/><path id="Rectangle-206" fill="#DBAF88" d="M12 15h50v300H12z"/><path id="Rectangle-207" fill="#DBAF88" d="M177.5-115.5h50v311h-50z" transform="rotate(-90 202.5 40)"/><text id="Introduction" fill="#643B0C" font-family="OpenSans-Bold, Open Sans" font-size="32" font-weight="bold"><tspan x="102" y="134">Introduction</tspan> <tspan x="102" y="183" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">This Ecma Standard is based on </tspan> <tspan x="102" y="216" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">several originating </tspan> <tspan x="102" y="249" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">technologies, the most well </tspan> <tspan x="102" y="282" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">known being JavaScript </tspan> <tspan x="102" y="315" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">(Netscape) and JScript </tspan> <tspan x="102" y="348" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">(Microsoft). The language was </tspan> <tspan x="102" y="381" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">invented by Brendan Eich at </tspan> <tspan x="102" y="414" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">Netscape and first appeared in </tspan> <tspan x="102" y="447" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">that company’s Navigator 2.0 </tspan> <tspan x="102" y="480" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">browser. It has appeared in all </tspan> <tspan x="102" y="513" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">subsequent browsers from </tspan> <tspan x="102" y="546" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">Netscape and in all browsers </tspan> <tspan x="102" y="579" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">from Microsoft starting with </tspan> <tspan x="102" y="612" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">Internet Explorer 3.0.</tspan> <tspan x="102" y="645" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">The development of this </tspan> <tspan x="102" y="678" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">Standard started in November </tspan> <tspan x="102" y="711" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">1996. The first edition of this </tspan> <tspan x="102" y="744" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">Ecma Standard was adopted by </tspan> <tspan x="102" y="777" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">the Ecma General Assembly of </tspan> <tspan x="102" y="810" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal">June 1997.</tspan></text><path id="Rectangle-5" fill="url(#linearGradient-1)" d="M42-7h275v447H42z" transform="rotate(-180 179.5 216.5)"/><path id="Rectangle-6" fill="#FFF" d="M316 8h162v399H316z"/><path id="Line-7" fill="#C06334" fill-rule="nonzero" d="M62.41 15.68l7 14h-6.001v20h6.001l-7 14-7-14h5.999v-20H55.41l7-14z"/><path id="Line-28" fill="#C06334" fill-rule="nonzero" d="M46.41 57.68l14 7-14 7-.001-6h-20l.001 6-14-7 14-7-.001 6h20l.001-6z"/><text id="clientTop:25px-=-bor" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="75.7" y="43" fill="#C06334">clientTop:</tspan> <tspan x="147.7" y="43" fill="#1C85B5">25px </tspan> <tspan x="183.7" y="43" fill="#C06334">= border</tspan></text><text id="clientLeft:25px" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal" transform="rotate(-90 37.32 136)"><tspan x="-16.68" y="140" fill="#C06334">clientLeft:</tspan> <tspan x="62.52" y="140" fill="#1C85B5">25px</tspan></text><g id="Rectangle-8-+-Rectangle-7" transform="translate(-45 -3)"><path id="Rectangle-8" fill="url(#linearGradient-1)" d="M86.5-85.5h275v447h-275z" transform="rotate(-90 224 138)"/><path id="Rectangle-7" fill="#FFF" d="M152 156h162v399H152z" transform="rotate(90 233 355.5)"/></g></g></g></svg> \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-left-top@2x.png b/2-ui/1-document/09-size-and-scroll/metric-client-left-top@2x.png deleted file mode 100644 index a8e170eabf..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-left-top@2x.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-width-height.png b/2-ui/1-document/09-size-and-scroll/metric-client-width-height.png deleted file mode 100644 index ed44652d89..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-width-height.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-width-height.svg b/2-ui/1-document/09-size-and-scroll/metric-client-width-height.svg new file mode 100644 index 0000000000..83864b4c57 --- /dev/null +++ b/2-ui/1-document/09-size-and-scroll/metric-client-width-height.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="500" height="493" viewBox="0 0 500 493"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><linearGradient id="linearGradient-1" x1="50%" x2="50%" y1="0%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#D1CFCD"/></linearGradient><linearGradient id="linearGradient-2" x1="50%" x2="50%" y1="0%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#D1CFCD"/></linearGradient></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="metric-client-width-height.svg"><path id="Rectangle-2" fill="#DBAF88" d="M411 130v290H21V130h390zm-25 25H46v240h340V155z"/><path id="Rectangle-1" stroke="#C06334" stroke-width="2" d="M350 177v199H67V177h283z"/><g id="Group" transform="translate(370 155)"><rect id="Rectangle-19" width="15" height="239" x=".5" y=".5" fill="#D1CFCD" stroke="#D1CFCD" rx="3"/><g id="Rectangle-18-+-Triangle-1"><rect id="Rectangle-18" width="15" height="19" x=".5" y=".5" fill="url(#linearGradient-1)" stroke="#D1CFCD" rx="3"/><path id="Triangle-1" fill="#7E7C7B" d="M8 7l3.2 6H4.8z"/></g><g id="Rectangle-18-+-Triangle-2" transform="matrix(1 0 0 -1 0 240)"><rect id="Rectangle-18" width="15" height="19" x=".5" y=".5" fill="url(#linearGradient-1)" stroke="#D1CFCD" rx="3"/><path id="Triangle-1" fill="#7E7C7B" d="M8 7l3.2 6H4.8z"/></g><g id="Rectangle-18-+-Triangle-3-+-Group" transform="translate(0 50)"><g id="Rectangle-18-+-Triangle-3" fill="url(#linearGradient-2)" stroke="#D1CFCD" transform="matrix(1 0 0 -1 0 51)"><rect id="Rectangle-18" width="15" height="50" x=".5" y=".5" rx="3"/></g><g id="Group" fill="#D1CFCD" stroke="#7E7C7B" transform="translate(4 20)"><path id="Rectangle-22" d="M.5.5h7v1h-7z"/><path id="Rectangle-23" d="M.5 3.5h7v1h-7z"/><path id="Rectangle-24" d="M.5 6.5h7v1h-7z"/><path id="Rectangle-25" d="M.5 9.5h7v1h-7z"/></g></g></g><text id="border" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="12.9" y="75" fill="#C06334">border</tspan> <tspan x="20.1" y="89" fill="#1C85B5">25px</tspan></text><text id="padding" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="32.3" y="45" fill="#C06334">padding</tspan> <tspan x="43.1" y="59" fill="#1C85B5">20px</tspan></text><text id="content-width:284px" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="139.1" y="109" fill="#C06334">content width:</tspan> <tspan x="239.9" y="109" fill="#1C85B5">284px</tspan></text><path id="Line-15" fill="#C06334" fill-rule="nonzero" d="M336.5 110l14 7-14 7v-6H82.679l.001 6-14-7 14-7-.001 6H336.5v-6z"/><path id="Line-14" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M21.48 93v43"/><path id="Line-13" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M47.48 93v41"/><text id="border-2" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="376.9" y="76" fill="#C06334">border</tspan> <tspan x="384.1" y="90" fill="#1C85B5">25px</tspan></text><text id="padding-2" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="339.3" y="16" fill="#C06334">padding</tspan> <tspan x="350.1" y="30" fill="#1C85B5">20px</tspan></text><text id="scrollbar" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="349.1" y="48" fill="#C06334">scrollbar</tspan> <tspan x="367.1" y="62" fill="#1C85B5">16px</tspan></text><path id="Line-17" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M371.48 93v43"/><path id="Line-20" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M385.48 93v43"/><path id="Line-18" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M410.48 93v41"/><path id="Line-16" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M66.48 93v41"/><path id="Line-19" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M353.48 93v41"/><text id="clientWidth-=-20+284" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="80.3" y="473" fill="#C06334">clientWidth = </tspan> <tspan x="197.9" y="473" fill="#1C85B5">20+284+20 </tspan> <tspan x="281.9" y="473" fill="#C06334">=</tspan> <tspan x="290.3" y="473" fill="#1C85B5"> 324px</tspan></text><path id="Line-24" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M46.5 376v88.142"/><path id="Line-25" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M370.5 376v88.142"/><path id="Line-22" fill="#C06334" fill-rule="nonzero" d="M354 445.071l14 7-14 7v-6.001H62v6.001l-14-7 14-7v5.999h292v-5.999z"/><text id="clientHeight:240px" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-90 488.5 277.5)"><tspan x="412.9" y="282" fill="#C06334">clientHeight:</tspan> <tspan x="522.1" y="282" fill="#1C85B5">240px</tspan></text><path id="Line-27" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M365.5 156h120"/><path id="Line-28" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M365.5 394h120"/><path id="Line-26" fill="#C06334" fill-rule="nonzero" d="M476 160l7 14-6-.001V375h6l-7 14-7-14h6V173.999l-6 .001 7-14z"/><text id="height:200px" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal" transform="rotate(-90 422.5 274)"><tspan x="379.3" y="278" fill="#C06334">height:</tspan> <tspan x="429.7" y="278" fill="#1C85B5">200px</tspan></text><path id="Line-3" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M353.5 175h88.142"/><path id="Line-2" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M353.5 377h88.142"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M435 177.5l7 14h-6V360h6l-7 14-7-14h6V191.5h-6l7-14z"/><text id="Introduction" fill="#643B0C" font-family="OpenSans-Bold, Open Sans" font-size="16" font-weight="bold"><tspan x="69" y="193">Introduction</tspan> <tspan x="69" y="221" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">This Ecma Standard is based on several </tspan> <tspan x="69" y="240" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">originating technologies, the most well </tspan> <tspan x="69" y="259" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">known being JavaScript (Netscape) and </tspan> <tspan x="69" y="278" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">JScript (Microsoft). The language was </tspan> <tspan x="69" y="297" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">invented by Brendan Eich at Netscape and </tspan> <tspan x="69" y="316" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">first appeared in that company’s Navigator </tspan> <tspan x="69" y="335" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">2.0 browser. It has appeared in all </tspan> <tspan x="69" y="354" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">subsequent browsers from Netscape and </tspan> <tspan x="69" y="373" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">in all browsers from Microsoft starting with </tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-width-height@2x.png b/2-ui/1-document/09-size-and-scroll/metric-client-width-height@2x.png deleted file mode 100644 index f31f483e46..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-width-height@2x.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding.png b/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding.png deleted file mode 100644 index a183c6b384..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding.svg b/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding.svg new file mode 100644 index 0000000000..330d2a7c04 --- /dev/null +++ b/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="409" height="467" viewBox="0 0 409 467"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><linearGradient id="linearGradient-1" x1="50%" x2="50%" y1="0%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#D1CFCD"/></linearGradient><linearGradient id="linearGradient-2" x1="50%" x2="50%" y1="0%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#D1CFCD"/></linearGradient></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="metric-client-width-nopadding.svg"><path fill="#FFF" d="M0 0h409v467H0z"/><path id="Rectangle-1" fill="#DBAF88" d="M389 131v250H35V131h354zm-25 25H60v200h304V156z"/><text id="clientWidth:284px-=-" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="84.7" y="399.642" fill="#C06334">clientWidth:</tspan> <tspan x="171.1" y="399.642" fill="#1C85B5">284px </tspan> <tspan x="214.3" y="399.642" fill="#C06334">= content width</tspan></text><path id="Line-44" fill="#C06334" fill-rule="nonzero" d="M329.68 402.642l14 7-14 7-.001-6.001h-252l.001 6-14-7 14-7-.001 6h252l.001-6z"/><text id="CSS-width:-300px" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="145.9" y="429.642" fill="#C06334">CSS width:</tspan> <tspan x="217.9" y="429.642" fill="#1C85B5"> 300px</tspan></text><path id="Line-47" fill="#C06334" fill-rule="nonzero" d="M349 427.642l14 7-14 7v-6.001H76.679l.001 6-14-7 14-7-.001 6H349v-6z"/><text id="Introduction-This-Ec" fill="#643B0C" font-family="OpenSans-Bold, Open Sans" font-size="16" font-weight="bold"><tspan x="62" y="173">Introduction</tspan> <tspan x="62" y="201" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">This Ecma Standard is based on several </tspan> <tspan x="62" y="220" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">originating technologies, the most well </tspan> <tspan x="62" y="239" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">known being JavaScript (Netscape) and </tspan> <tspan x="62" y="258" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">JScript (Microsoft). The language was </tspan> <tspan x="62" y="277" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">invented by Brendan Eich at Netscape and </tspan> <tspan x="62" y="296" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">first appeared in that company’s Navigator </tspan> <tspan x="62" y="315" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">2.0 browser. It has appeared in all </tspan> <tspan x="62" y="334" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">subsequent browsers from Netscape and </tspan> <tspan x="62" y="353" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">in all browsers from Microsoft starting with </tspan></text><ellipse id="Oval-5" cx="191" cy="58.858" fill="#FBF2EC" stroke="#C06334" stroke-width="2" rx="76" ry="43.5"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M192 103v11.638h6l-7 14-7-14h6V103h2z"/><text id="padding:-0;" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="136" y="52">padding: 0;</tspan> <tspan x="136" y="70">width: 300px;</tspan></text><g id="Group-2" transform="translate(348 156)"><rect id="Rectangle-19" width="15" height="199" x=".5" y=".5" fill="#D1CFCD" stroke="#D1CFCD" rx="3"/><g id="Rectangle-18-+-Triangle-1"><rect id="Rectangle-18" width="15" height="19" x=".5" y=".5" fill="url(#linearGradient-1)" stroke="#D1CFCD" rx="3"/><path id="Triangle-1" fill="#7E7C7B" d="M8 7l3.2 6H4.8z"/></g><g id="Rectangle-18-+-Triangle-2" transform="matrix(1 0 0 -1 0 200)"><rect id="Rectangle-18" width="15" height="19" x=".5" y=".5" fill="url(#linearGradient-1)" stroke="#D1CFCD" rx="3"/><path id="Triangle-1" fill="#7E7C7B" d="M8 7l3.2 6H4.8z"/></g><g id="Rectangle-18-+-Triangle-3-+-Group" transform="translate(0 50)"><g id="Rectangle-18-+-Triangle-3" fill="url(#linearGradient-2)" stroke="#D1CFCD" transform="matrix(1 0 0 -1 0 51)"><rect id="Rectangle-18" width="15" height="50" x=".5" y=".5" rx="3"/></g><g id="Group" fill="#D1CFCD" stroke="#7E7C7B" transform="translate(4 20)"><path id="Rectangle-22" d="M.5.5h7v1h-7z"/><path id="Rectangle-23" d="M.5 3.5h7v1h-7z"/><path id="Rectangle-24" d="M.5 6.5h7v1h-7z"/><path id="Rectangle-25" d="M.5 9.5h7v1h-7z"/></g></g></g><path id="Line-46" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M60.5 354.642v96"/><path id="Line-45" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M345.5 354.642V414"/><path id="Line-48" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M363.5 354.642v96"/></g></g></svg> \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding@2x.png b/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding@2x.png deleted file mode 100644 index 5a73f87a97..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-client-width-nopadding@2x.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-css.png b/2-ui/1-document/09-size-and-scroll/metric-css.png deleted file mode 100644 index 564c210a99..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-css.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-css.svg b/2-ui/1-document/09-size-and-scroll/metric-css.svg new file mode 100644 index 0000000000..13aa62afd0 --- /dev/null +++ b/2-ui/1-document/09-size-and-scroll/metric-css.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="566" height="469" viewBox="0 0 566 469"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><linearGradient id="linearGradient-1" x1="50%" x2="50%" y1="0%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#D1CFCD"/></linearGradient><linearGradient id="linearGradient-2" x1="50%" x2="50%" y1="0%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#D1CFCD"/></linearGradient></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="metric-css.svg"><path id="Rectangle-1" stroke="#C06334" stroke-width="2" d="M460 184v199H177V184h283z"/><path id="Rectangle-1" fill="#DBAF88" d="M521 139v290H131V139h390zm-25 25H156v240h340V164z"/><g id="Group" transform="translate(480 163)"><rect id="Rectangle-19" width="15" height="239" x=".5" y=".5" fill="#D1CFCD" stroke="#D1CFCD" rx="3"/><g id="Rectangle-18-+-Triangle-1"><rect id="Rectangle-18" width="15" height="19" x=".5" y=".5" fill="url(#linearGradient-1)" stroke="#D1CFCD" rx="3"/><path id="Triangle-1" fill="#7E7C7B" d="M8 7l3.2 6H4.8z"/></g><g id="Rectangle-18-+-Triangle-2" transform="matrix(1 0 0 -1 0 240)"><rect id="Rectangle-18" width="15" height="19" x=".5" y=".5" fill="url(#linearGradient-1)" stroke="#D1CFCD" rx="3"/><path id="Triangle-1" fill="#7E7C7B" d="M8 7l3.2 6H4.8z"/></g><g id="Rectangle-18-+-Triangle-3-+-Group" transform="translate(0 50)"><g id="Rectangle-18-+-Triangle-3" fill="url(#linearGradient-2)" stroke="#D1CFCD" transform="matrix(1 0 0 -1 0 51)"><rect id="Rectangle-18" width="15" height="50" x=".5" y=".5" rx="3"/></g><g id="Group" fill="#D1CFCD" stroke="#7E7C7B" transform="translate(4 20)"><path id="Rectangle-22" d="M.5.5h7v1h-7z"/><path id="Rectangle-23" d="M.5 3.5h7v1h-7z"/><path id="Rectangle-24" d="M.5 6.5h7v1h-7z"/><path id="Rectangle-25" d="M.5 9.5h7v1h-7z"/></g></g></g><text id="padding:20px" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="7" y="176" fill="#C06334">padding:</tspan> <tspan x="64.6" y="176" fill="#1C85B5">20px</tspan></text><text id="height:200px" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal" transform="rotate(-90 530.5 282)"><tspan x="487.3" y="286" fill="#C06334">height:</tspan> <tspan x="537.7" y="286" fill="#1C85B5">200px</tspan></text><path id="Line" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M461.5 183h88.142"/><path id="Line-3" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M93.5 184h88.142"/><path id="Line-4" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M93.5 162h88.142"/><path id="Line-2" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M461.5 385h88.142"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M543 185.5l7 14h-6V368h6l-7 14-7-14h6V199.5h-6l7-14z"/><text id="padding:20px-2" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="7" y="396" fill="#C06334">padding:</tspan> <tspan x="64.6" y="396" fill="#1C85B5">20px</tspan></text><path id="Line-5" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M93.5 404h88.142"/><path id="Line-6" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M93.5 382h88.142"/><text id="border" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="122.9" y="83" fill="#C06334">border</tspan> <tspan x="130.1" y="97" fill="#1C85B5">25px</tspan></text><text id="padding" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="142.3" y="53" fill="#C06334">padding</tspan> <tspan x="153.1" y="67" fill="#1C85B5">20px</tspan></text><text id="content-width:284px" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="249.1" y="117" fill="#C06334">content width:</tspan> <tspan x="349.9" y="117" fill="#1C85B5">284px</tspan></text><path id="Line-21" fill="#C06334" fill-rule="nonzero" d="M446.5 118l14 7-14 7v-6H192.679l.001 6-14-7 14-7-.001 6H446.5v-6z"/><path id="Line-14" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M131.48 101v43"/><path id="Line-13" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M157.48 101v41"/><text id="border-2" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="486.9" y="84" fill="#C06334">border</tspan> <tspan x="494.1" y="98" fill="#1C85B5">25px</tspan></text><text id="padding-2" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="449.3" y="24" fill="#C06334">padding</tspan> <tspan x="460.1" y="38" fill="#1C85B5">20px</tspan></text><text id="scrollbar" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="459.1" y="56" fill="#C06334">scrollbar</tspan> <tspan x="477.1" y="70" fill="#1C85B5">16px</tspan></text><text id="Introduction" fill="#643B0C" font-family="OpenSans-Bold, Open Sans" font-size="16" font-weight="bold"><tspan x="178" y="200">Introduction</tspan> <tspan x="178" y="228" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">This Ecma Standard is based on several </tspan> <tspan x="178" y="247" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">originating technologies, the most well </tspan> <tspan x="178" y="266" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">known being JavaScript (Netscape) and </tspan> <tspan x="178" y="285" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">JScript (Microsoft). The language was </tspan> <tspan x="178" y="304" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">invented by Brendan Eich at Netscape and </tspan> <tspan x="178" y="323" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">first appeared in that company’s Navigator </tspan> <tspan x="178" y="342" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">2.0 browser. It has appeared in all </tspan> <tspan x="178" y="361" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">subsequent browsers from Netscape and </tspan> <tspan x="178" y="380" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">in all browsers from Microsoft starting with</tspan> <tspan x="178" y="399" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"> </tspan></text><path id="Line-17" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M481.48 101v43"/><path id="Line-20" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M495.48 101v43"/><path id="Line-18" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M520.48 101v41"/><path id="Line-16" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M176.48 101v41"/><path id="Line-19" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M463.48 101v41"/></g></g></svg> \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/metric-css@2x.png b/2-ui/1-document/09-size-and-scroll/metric-css@2x.png deleted file mode 100644 index e4fd7bb878..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-css@2x.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png b/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png deleted file mode 100644 index 01d2d00087..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-offset-parent.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-parent.svg b/2-ui/1-document/09-size-and-scroll/metric-offset-parent.svg new file mode 100644 index 0000000000..9e247639be --- /dev/null +++ b/2-ui/1-document/09-size-and-scroll/metric-offset-parent.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="597" height="520" viewBox="0 0 597 520"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><linearGradient id="linearGradient-3" x1="50%" x2="50%" y1="0%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#D1CFCD"/></linearGradient><linearGradient id="linearGradient-4" x1="50%" x2="50%" y1="0%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#D1CFCD"/></linearGradient><pattern id="pattern-1" width="30" height="30" x="-12" y="-6" patternUnits="userSpaceOnUse"><use xlink:href="#image-2"/></pattern><image id="image-2" width="30" height="30" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAHqADAAQAAAABAAAAHgAAAADKQTcFAAAA/UlEQVRIDe3VXQ6DIAwA4LUea7uCIeFdD+f2bDReYbvGbqLOmmEEAQ2T8jJeDEnTr+VPaJrmnWVZmef58+IYbdtex3G8nxkHMZIeKRKoyRT4DKfAF5gb12BOfANz4VaYA3fCsXHsuu5GiG0IIV4AUPR9X50dhzGSHiny/3Kd+iPwvdnaqeZ8szWYTvYXfwzDUEgp936VwXFoXiO6QoQiYlXXtfeq/RK36VgVQijhU/LS13lonBOmAkKTquLN7zof0sQMUHPqlDreW/aQuCmnfy9Dkh5pBtbtx9hLtXrmd97jFPhyuLjxBaal4MQ1mBPfwFy4FebAnXBsPNnL9QFH9tNXxu42ugAAAABJRU5ErkJggg=="/></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="metric-offset-parent.svg"><path fill="#FFF" d="M0 0h597v520H0z"/><path id="Rectangle-10" fill="url(#pattern-1)" fill-opacity=".5" stroke="#D1CFCD" stroke-width="3" d="M19.5 25.5h558v479h-558z"/><path id="Rectangle-1" fill="#DBAF88" d="M552 185v290H178V185h374zm-25 25H203v240h324V210z"/><text id="offsetTop:180px" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-90 163 104.32)"><tspan x="100" y="108.82" fill="#C06334">offsetTop:</tspan> <tspan x="184" y="108.82" fill="#1C85B5">180px</tspan></text><text id="offsetLeft:180px" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="34.3" y="179.32" fill="#C06334">offsetLeft:</tspan> <tspan x="126.7" y="179.32" fill="#1C85B5">180px</tspan></text><text id="Introduction" fill="#643B0C" font-family="OpenSans-Bold, Open Sans" font-size="16" font-weight="bold"><tspan x="223" y="247">Introduction</tspan> <tspan x="223" y="275" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">This Ecma Standard is based on several </tspan> <tspan x="223" y="294" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">originating technologies, the most well </tspan> <tspan x="223" y="313" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">known being JavaScript (Netscape) and </tspan> <tspan x="223" y="332" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">JScript (Microsoft). The language was </tspan> <tspan x="223" y="351" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">invented by Brendan Eich at Netscape </tspan> <tspan x="223" y="370" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">and first appeared in that company’s </tspan> <tspan x="223" y="389" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">Navigator 2.0 browser. It has appeared </tspan> <tspan x="223" y="408" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">in all subsequent browsers from </tspan> <tspan x="223" y="427" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">Netscape and in all browsers from </tspan> <tspan x="223" y="446" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">Microsoft</tspan></text><path id="Line-40" fill="#C06334" fill-rule="nonzero" d="M179 26.5l7 14h-6v128.82h6l-7 14-7-14h6V40.5h-6l7-14z"/><path id="Line-41" fill="#C06334" fill-rule="nonzero" d="M163.41 179.91l14 7-14 7v-6H34.589l.001 6-14-7 14-7-.001 6H163.41v-6z"/><circle id="Oval-2" cx="203" cy="211" r="2" fill="#1C85B5"/><circle id="Oval-2" cx="179" cy="186" r="3" fill="#1C85B5"/><ellipse id="Oval-6" cx="364.25" cy="104" fill="#FBF2EC" stroke="#C06334" stroke-width="2" rx="100" ry="50"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M366 156.379v11.638h6l-7 14-7-14h6v-11.638h2z"/><text id="position:-absolute;" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="296.5" y="95.793">position: absolute;</tspan> <tspan x="296.5" y="110.793">left: 180px;</tspan> <tspan x="296.5" y="125.793">top: 180px;</tspan></text><text id="offsetParent-<MAIN>-" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold"><tspan x="20.5" y="19.379">offsetParent <MAIN> </tspan></text><text id="<DIV>" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="16" font-weight="bold"><tspan x="183.5" y="180.379"><DIV></tspan></text><g id="Group" transform="translate(511 210)"><rect id="Rectangle-19" width="15" height="239" x=".5" y=".5" fill="#D1CFCD" stroke="#D1CFCD" rx="3"/><g id="Rectangle-18-+-Triangle-1"><rect id="Rectangle-18" width="15" height="19" x=".5" y=".5" fill="url(#linearGradient-3)" stroke="#D1CFCD" rx="3"/><path id="Triangle-1" fill="#7E7C7B" d="M8 7l3.2 6H4.8z"/></g><g id="Rectangle-18-+-Triangle-2" transform="matrix(1 0 0 -1 0 240)"><rect id="Rectangle-18" width="15" height="19" x=".5" y=".5" fill="url(#linearGradient-3)" stroke="#D1CFCD" rx="3"/><path id="Triangle-1" fill="#7E7C7B" d="M8 7l3.2 6H4.8z"/></g><g id="Rectangle-18-+-Triangle-3-+-Group" transform="translate(0 50)"><g id="Rectangle-18-+-Triangle-3" fill="url(#linearGradient-4)" stroke="#D1CFCD" transform="matrix(1 0 0 -1 0 51)"><rect id="Rectangle-18" width="15" height="50" x=".5" y=".5" rx="3"/></g><g id="Group" fill="#D1CFCD" stroke="#7E7C7B" transform="translate(4 20)"><path id="Rectangle-22" d="M.5.5h7v1h-7z"/><path id="Rectangle-23" d="M.5 3.5h7v1h-7z"/><path id="Rectangle-24" d="M.5 6.5h7v1h-7z"/><path id="Rectangle-25" d="M.5 9.5h7v1h-7z"/></g></g></g></g></g></svg> \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png b/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png deleted file mode 100644 index 125a91e153..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-offset-parent@2x.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-width-height.png b/2-ui/1-document/09-size-and-scroll/metric-offset-width-height.png deleted file mode 100644 index a13075f03a..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-offset-width-height.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-width-height.svg b/2-ui/1-document/09-size-and-scroll/metric-offset-width-height.svg new file mode 100644 index 0000000000..49bdccda78 --- /dev/null +++ b/2-ui/1-document/09-size-and-scroll/metric-offset-width-height.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="508" height="509" viewBox="0 0 508 509"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><linearGradient id="linearGradient-1" x1="50%" x2="50%" y1="0%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#D1CFCD"/></linearGradient><linearGradient id="linearGradient-2" x1="50%" x2="50%" y1="0%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#D1CFCD"/></linearGradient></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="metric-offset-width-height.svg"><path id="Rectangle-2" fill="#DBAF88" d="M420 130v290H30V130h390zm-25 25H55v240h340V155z"/><path id="Rectangle-1" stroke="#C06334" stroke-width="2" d="M359 177v199H76V177h283z"/><g id="Group" transform="translate(379 155)"><rect id="Rectangle-19" width="15" height="239" x=".5" y=".5" fill="#D1CFCD" stroke="#D1CFCD" rx="3"/><g id="Rectangle-18-+-Triangle-1"><rect id="Rectangle-18" width="15" height="19" x=".5" y=".5" fill="url(#linearGradient-1)" stroke="#D1CFCD" rx="3"/><path id="Triangle-1" fill="#7E7C7B" d="M8 7l3.2 6H4.8z"/></g><g id="Rectangle-18-+-Triangle-2" transform="matrix(1 0 0 -1 0 240)"><rect id="Rectangle-18" width="15" height="19" x=".5" y=".5" fill="url(#linearGradient-1)" stroke="#D1CFCD" rx="3"/><path id="Triangle-1" fill="#7E7C7B" d="M8 7l3.2 6H4.8z"/></g><g id="Rectangle-18-+-Triangle-3-+-Group" transform="translate(0 50)"><g id="Rectangle-18-+-Triangle-3" fill="url(#linearGradient-2)" stroke="#D1CFCD" transform="matrix(1 0 0 -1 0 51)"><rect id="Rectangle-18" width="15" height="50" x=".5" y=".5" rx="3"/></g><g id="Group" fill="#D1CFCD" stroke="#7E7C7B" transform="translate(4 20)"><path id="Rectangle-22" d="M.5.5h7v1h-7z"/><path id="Rectangle-23" d="M.5 3.5h7v1h-7z"/><path id="Rectangle-24" d="M.5 6.5h7v1h-7z"/><path id="Rectangle-25" d="M.5 9.5h7v1h-7z"/></g></g></g><text id="border" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="21.9" y="75" fill="#C06334">border</tspan> <tspan x="29.1" y="89" fill="#1C85B5">25px</tspan></text><text id="padding" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="41.3" y="45" fill="#C06334">padding</tspan> <tspan x="52.1" y="59" fill="#1C85B5">20px</tspan></text><text id="content-width:284px" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="148.1" y="109" fill="#C06334">content width:</tspan> <tspan x="248.9" y="109" fill="#1C85B5">284px</tspan></text><text id="height:200px" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal" transform="rotate(-90 429.5 274)"><tspan x="386.3" y="278" fill="#C06334">height:</tspan> <tspan x="436.7" y="278" fill="#1C85B5">200px</tspan></text><path id="Line" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M360.5 175h88.142"/><path id="Line-2" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M360.5 377h88.142"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M442 177.5l7 14h-6V360h6l-7 14-7-14h6V191.5h-6l7-14z"/><path id="Line-15" fill="#C06334" fill-rule="nonzero" d="M345.5 110l14 7-14 7v-6H91.679l.001 6-14-7 14-7-.001 6H345.5v-6z"/><path id="Line-14" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M30.48 93v43"/><path id="Line-13" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M56.48 93v41"/><text id="border-2" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="385.9" y="76" fill="#C06334">border</tspan> <tspan x="393.1" y="90" fill="#1C85B5">25px</tspan></text><text id="padding-2" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="348.3" y="16" fill="#C06334">padding</tspan> <tspan x="359.1" y="30" fill="#1C85B5">20px</tspan></text><text id="scrollbar" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="358.1" y="48" fill="#C06334">scrollbar</tspan> <tspan x="376.1" y="62" fill="#1C85B5">16px</tspan></text><path id="Line-17" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M380.48 93v43"/><path id="Line-20" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M394.48 93v43"/><path id="Line-18" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M419.48 93v41"/><path id="Line-16" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M75.48 93v41"/><path id="Line-19" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M362.48 93v41"/><text id="offsetWidth-=-25+20+" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="56.5" y="484" fill="#C06334">offsetWidth = </tspan> <tspan x="174.1" y="484" fill="#1C85B5">25+20+284+20+16+25 </tspan> <tspan x="333.7" y="484" fill="#C06334">=</tspan> <tspan x="342.1" y="484" fill="#1C85B5"> 390px</tspan></text><path id="Line-24" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M30.5 419v78"/><path id="Line-25" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M419.5 419v80"/><path id="Line-22" fill="#C06334" fill-rule="nonzero" d="M403 456.071l14 7-14 7v-6.001H47.089l.001 6.001-14-7 14-7-.001 5.999H403v-5.999z"/><text id="offsetHeight:290px" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-90 483.5 280.5)"><tspan x="407.9" y="285" fill="#C06334">offsetHeight:</tspan> <tspan x="517.1" y="285" fill="#1C85B5">290px</tspan></text><path id="Line-27" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M417.5 131h88.142"/><path id="Line-28" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M417.5 420h88.142"/><path id="Line-26" fill="#C06334" fill-rule="nonzero" d="M469 132.59l7 14-6-.001V405.41h6l-7 14-7-14h6V146.589l-6 .001 7-14z"/><text id="Introduction" fill="#643B0C" font-family="OpenSans-Bold, Open Sans" font-size="16" font-weight="bold"><tspan x="79" y="193">Introduction</tspan> <tspan x="79" y="221" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">This Ecma Standard is based on several </tspan> <tspan x="79" y="240" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">originating technologies, the most well </tspan> <tspan x="79" y="259" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">known being JavaScript (Netscape) and </tspan> <tspan x="79" y="278" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">JScript (Microsoft). The language was </tspan> <tspan x="79" y="297" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">invented by Brendan Eich at Netscape and </tspan> <tspan x="79" y="316" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">first appeared in that company’s Navigator </tspan> <tspan x="79" y="335" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">2.0 browser. It has appeared in all </tspan> <tspan x="79" y="354" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">subsequent browsers from Netscape and </tspan> <tspan x="79" y="373" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">in all browsers from Microsoft starting </tspan> <tspan x="79" y="392" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">with </tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/metric-offset-width-height@2x.png b/2-ui/1-document/09-size-and-scroll/metric-offset-width-height@2x.png deleted file mode 100644 index 7bfb24c221..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-offset-width-height@2x.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-scroll-top.png b/2-ui/1-document/09-size-and-scroll/metric-scroll-top.png deleted file mode 100644 index 4bc795aa0e..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-scroll-top.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-scroll-top.svg b/2-ui/1-document/09-size-and-scroll/metric-scroll-top.svg new file mode 100644 index 0000000000..c6d14d0f38 --- /dev/null +++ b/2-ui/1-document/09-size-and-scroll/metric-scroll-top.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="489" height="542" viewBox="0 0 489 542"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><linearGradient id="linearGradient-1" x1="50%" x2="50%" y1="0%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#D1CFCD"/></linearGradient><linearGradient id="linearGradient-2" x1="50%" x2="50%" y1="0%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#D1CFCD"/></linearGradient></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="metric-scroll-top.svg"><path fill="#FFF" d="M0 0h489v542H0z"/><text id="Introduction" fill="#643B0C" font-family="OpenSans-Bold, Open Sans" font-size="16" font-weight="bold"><tspan x="94" y="54">Introduction</tspan> <tspan x="94" y="82" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">This Ecma Standard is based on several </tspan> <tspan x="94" y="101" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">originating technologies, the most well </tspan> <tspan x="94" y="120" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">known being JavaScript (Netscape) and </tspan> <tspan x="94" y="139" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">JScript (Microsoft). The language was </tspan> <tspan x="94" y="158" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">invented by Brendan Eich at Netscape and </tspan> <tspan x="94" y="177" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">first appeared in that company’s Navigator </tspan> <tspan x="94" y="196" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">2.0 browser. It has appeared in all </tspan> <tspan x="94" y="215" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">subsequent browsers from Netscape and </tspan> <tspan x="94" y="234" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">in all browsers from Microsoft starting with </tspan> <tspan x="94" y="253" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">Internet Explorer 3.0.</tspan> <tspan x="94" y="272" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">The development of this Standard started </tspan> <tspan x="94" y="291" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">in November 1996. The first edition of this </tspan> <tspan x="94" y="310" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">Ecma Standard was adopted by the Ecma </tspan> <tspan x="94" y="329" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">General Assembly of June 1997.</tspan> <tspan x="94" y="348" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">That Ecma Standard was submitted to ISO/</tspan> <tspan x="94" y="367" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">IEC JTC 1 for adoption under the fast-track </tspan> <tspan x="94" y="386" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">procedure, and approved as international </tspan> <tspan x="94" y="405" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">standard ISO/IEC 16262, in April 1998. The </tspan> <tspan x="94" y="424" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">Ecma General Assembly of June 1998 </tspan> <tspan x="94" y="443" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">approved the second edition of ECMA-262 </tspan> <tspan x="94" y="462" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">to keep it fully aligned with ISO/IEC 16262. </tspan> <tspan x="94" y="481" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">Changes between the first and the second </tspan> <tspan x="94" y="500" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">edition are editorial in nature.</tspan></text><path id="Rectangle-1" fill="#DBAF88" d="M425 122v290H51V122h374zm-25 25H76v240h324V147z"/><path id="Rectangle-2" stroke="#DBAF88" stroke-width="2" d="M75 22h326v500H75z"/><text id="scrollTop" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal" transform="rotate(-90 16.5 84)"><tspan x="-15.9" y="88">scrollTop</tspan></text><path id="Line-43" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M9.5 147h66.14"/><path id="Line-42" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M10.5 21h66.14"/><path id="Line-39" fill="#C06334" fill-rule="nonzero" d="M35 20.5l7 14h-6v97.819l6 .001-7 14-7-14 6-.001V34.5h-6l7-14z"/><path id="Rectangle-14" fill="#FFF" d="M88 33h312v89H88z"/><path id="Rectangle-15" fill="#FFF" d="M88 411h312v89H88z"/><text id="scrollHeight:723px" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-90 449 270)"><tspan x="373.4" y="274.5" fill="#C06334">scrollHeight:</tspan> <tspan x="482.6" y="274.5" fill="#1C85B5">723px</tspan></text><path id="Line-27" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M393.5 22h78.14"/><path id="Line-28" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M393.5 522h78.14"/><path id="Line-25" fill="#C06334" fill-rule="nonzero" d="M462 24.5l7 14h-6v466h6l-7 14-7-14h6v-466h-6l7-14z"/><g id="Group" transform="translate(384 147)"><rect id="Rectangle-19" width="15" height="239" x=".5" y=".5" fill="#D1CFCD" stroke="#D1CFCD" rx="3"/><g id="Rectangle-18-+-Triangle-1"><rect id="Rectangle-18" width="15" height="19" x=".5" y=".5" fill="url(#linearGradient-1)" stroke="#D1CFCD" rx="3"/><path id="Triangle-1" fill="#7E7C7B" d="M8 7l3.2 6H4.8z"/></g><g id="Rectangle-18-+-Triangle-2" transform="matrix(1 0 0 -1 0 240)"><rect id="Rectangle-18" width="15" height="19" x=".5" y=".5" fill="url(#linearGradient-1)" stroke="#D1CFCD" rx="3"/><path id="Triangle-1" fill="#7E7C7B" d="M8 7l3.2 6H4.8z"/></g><g id="Rectangle-18-+-Triangle-3-+-Group" transform="translate(0 50)"><g id="Rectangle-18-+-Triangle-3" fill="url(#linearGradient-2)" stroke="#D1CFCD" transform="matrix(1 0 0 -1 0 51)"><rect id="Rectangle-18" width="15" height="50" x=".5" y=".5" rx="3"/></g><g id="Group" fill="#D1CFCD" stroke="#7E7C7B" transform="translate(4 20)"><path id="Rectangle-22" d="M.5.5h7v1h-7z"/><path id="Rectangle-23" d="M.5 3.5h7v1h-7z"/><path id="Rectangle-24" d="M.5 6.5h7v1h-7z"/><path id="Rectangle-25" d="M.5 9.5h7v1h-7z"/></g></g></g></g></g></svg> \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/metric-scroll-top@2x.png b/2-ui/1-document/09-size-and-scroll/metric-scroll-top@2x.png deleted file mode 100644 index bf0cf711a9..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-scroll-top@2x.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height.png b/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height.png deleted file mode 100644 index 581c214437..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height.png and /dev/null differ diff --git a/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height.svg b/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height.svg new file mode 100644 index 0000000000..0c3d299528 --- /dev/null +++ b/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="463" height="524" viewBox="0 0 463 524"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><linearGradient id="linearGradient-1" x1="50%" x2="50%" y1="0%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#D1CFCD"/></linearGradient><linearGradient id="linearGradient-2" x1="50%" x2="50%" y1="0%" y2="100%"><stop offset="0%" stop-color="#FFF"/><stop offset="100%" stop-color="#D1CFCD"/></linearGradient></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="metric-scroll-width-height.svg"><path fill="#FFF" d="M0 0h463v524H0z"/><text id="Introduction" fill="#643B0C" font-family="OpenSans-Bold, Open Sans" font-size="16" font-weight="bold"><tspan x="66" y="54">Introduction</tspan> <tspan x="66" y="82" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">This Ecma Standard is based on several </tspan> <tspan x="66" y="101" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">originating technologies, the most well </tspan> <tspan x="66" y="120" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">known being JavaScript (Netscape) and </tspan> <tspan x="66" y="139" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">JScript (Microsoft). The language was </tspan> <tspan x="66" y="158" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">invented by Brendan Eich at Netscape </tspan> <tspan x="66" y="177" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">and first appeared in that company’s </tspan> <tspan x="66" y="196" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">Navigator 2.0 browser. It has appeared </tspan> <tspan x="66" y="215" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">in all subsequent browsers from </tspan> <tspan x="66" y="234" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">Netscape and in all browsers from </tspan> <tspan x="66" y="253" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">Microsoft starting with Internet Explorer </tspan> <tspan x="66" y="272" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">3.0.</tspan> <tspan x="66" y="291" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">The development of this Standard </tspan> <tspan x="66" y="310" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">started in November 1996. The first </tspan> <tspan x="66" y="329" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">edition of this Ecma Standard was </tspan> <tspan x="66" y="348" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">adopted by the Ecma General Assembly </tspan> <tspan x="66" y="367" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">of June 1997.</tspan> <tspan x="66" y="386" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">That Ecma Standard was submitted to </tspan> <tspan x="66" y="405" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">ISO/IEC JTC 1 for adoption under the </tspan> <tspan x="66" y="424" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">fast-track procedure, and approved as </tspan> <tspan x="66" y="443" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">international standard ISO/IEC 16262, in </tspan> <tspan x="66" y="462" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">April 1998. The Ecma General Assembly </tspan> <tspan x="66" y="481" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">of June 1998 approved the second </tspan> <tspan x="66" y="500" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">edition of ECMA-262 to keep it fully </tspan> <tspan x="66" y="519" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">aligned with ISO/IEC 16262. Changes </tspan> <tspan x="66" y="538" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">between the first and the second </tspan> <tspan x="66" y="557" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal">edition are editorial in nature.</tspan></text><path id="Rectangle-15" fill="#FFF" d="M58 410h312v111H58z"/><path id="Rectangle-14" fill="#FFF" d="M58 35h312v89H58z"/><path id="Rectangle-1" fill="#DBAF88" d="M395 123v290H21V123h374zm-25 25H46v240h324V148z"/><path id="Rectangle-2" stroke="#DBAF88" stroke-width="2" d="M45 22h326v502H45z"/><text id="scrollHeight:723px" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal" transform="rotate(-90 426 270.5)"><tspan x="350.4" y="275" fill="#C06334">scrollHeight:</tspan> <tspan x="459.6" y="275" fill="#1C85B5">723px</tspan></text><path id="Line-27" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M371 22h78"/><path id="Line-26" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M371 524h78"/><path id="Line-25" fill="#C06334" fill-rule="nonzero" d="M439 26l7 14h-6v466h6l-7 14-7-14h6V40h-6l7-14z"/><path id="Line-39" fill="#C06334" fill-rule="nonzero" d="M335.36 102l14 7-14 7-.001-6H64v6l-14-7 14-7v6h271.359l.001-6z"/><path id="Line-42" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M352 154v-54"/><path id="Line-43" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M47 154v-54"/><text id="scrollWidth-=-324px" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="149.2" y="105" fill="#C06334">scrollWidth = </tspan> <tspan x="266.8" y="105" fill="#1C85B5">324px</tspan></text><g id="Group" transform="translate(354 148)"><rect id="Rectangle-19" width="15" height="239" x=".5" y=".5" fill="#D1CFCD" stroke="#D1CFCD" rx="3"/><g id="Rectangle-18-+-Triangle-1"><rect id="Rectangle-18" width="15" height="19" x=".5" y=".5" fill="url(#linearGradient-1)" stroke="#D1CFCD" rx="3"/><path id="Triangle-1" fill="#7E7C7B" d="M8 7l3.2 6H4.8z"/></g><g id="Rectangle-18-+-Triangle-2" transform="matrix(1 0 0 -1 0 240)"><rect id="Rectangle-18" width="15" height="19" x=".5" y=".5" fill="url(#linearGradient-1)" stroke="#D1CFCD" rx="3"/><path id="Triangle-1" fill="#7E7C7B" d="M8 7l3.2 6H4.8z"/></g><g id="Rectangle-18-+-Triangle-3-+-Group" transform="translate(0 50)"><g id="Rectangle-18-+-Triangle-3" fill="url(#linearGradient-2)" stroke="#D1CFCD" transform="matrix(1 0 0 -1 0 51)"><rect id="Rectangle-18" width="15" height="50" x=".5" y=".5" rx="3"/></g><g id="Group" fill="#D1CFCD" stroke="#7E7C7B" transform="translate(4 20)"><path id="Rectangle-22" d="M.5.5h7v1h-7z"/><path id="Rectangle-23" d="M.5 3.5h7v1h-7z"/><path id="Rectangle-24" d="M.5 6.5h7v1h-7z"/><path id="Rectangle-25" d="M.5 9.5h7v1h-7z"/></g></g></g></g></g></svg> \ No newline at end of file diff --git a/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height@2x.png b/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height@2x.png deleted file mode 100644 index 1e2a20f546..0000000000 Binary files a/2-ui/1-document/09-size-and-scroll/metric-scroll-width-height@2x.png and /dev/null differ diff --git a/2-ui/1-document/10-size-and-scroll-window/article.md b/2-ui/1-document/10-size-and-scroll-window/article.md index e8fb386616..db904b534d 100644 --- a/2-ui/1-document/10-size-and-scroll-window/article.md +++ b/2-ui/1-document/10-size-and-scroll-window/article.md @@ -10,7 +10,7 @@ DOM の観点からは、ルートドキュメント要素は `document.document `document.documentElement` のプロパティ `clientWidth/clientHeight` はまさに私たちがここで欲しいものです: -![](document-client-width-height.png) +![](document-client-width-height.svg) ```online 例えば、このボタンはあなたのウィンドウの高さを表示します。: diff --git a/2-ui/1-document/10-size-and-scroll-window/document-client-width-height.png b/2-ui/1-document/10-size-and-scroll-window/document-client-width-height.png deleted file mode 100644 index 76a45a7a7f..0000000000 Binary files a/2-ui/1-document/10-size-and-scroll-window/document-client-width-height.png and /dev/null differ diff --git a/2-ui/1-document/10-size-and-scroll-window/document-client-width-height.svg b/2-ui/1-document/10-size-and-scroll-window/document-client-width-height.svg new file mode 100644 index 0000000000..65e77ae805 --- /dev/null +++ b/2-ui/1-document/10-size-and-scroll-window/document-client-width-height.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="508" height="203" viewBox="0 0 508 203"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="document-client-width-height.svg"><g id="noun_69008_cc" fill="#DBAF88" transform="translate(275 8)"><path id="Shape" d="M179.188 145H3.813C1.708 145 0 143.369 0 141.375V3.625C0 1.631 1.708 0 3.813 0h175.375C181.284 0 183 1.631 183 3.625v137.75c0 1.994-1.716 3.625-3.813 3.625zM7.625 137.75h167.75V7.25H7.625v130.5z"/><path id="Shape" d="M175.375 36.25H7.625c-2.104 0-3.813-1.631-3.813-3.625S5.521 29 7.626 29h167.75c2.097 0 3.813 1.631 3.813 3.625s-1.716 3.625-3.813 3.625zM61.32 126.578c-1.266 0-2.508-.595-3.233-1.682L34.892 89.849a3.486 3.486 0 01.16-4.082l23.195-29.906c1.25-1.631 3.63-1.964 5.33-.746 1.693 1.188 2.059 3.458.8 5.067l-21.646 27.92 21.807 32.951c1.12 1.69.587 3.937-1.197 4.988a3.936 3.936 0 01-2.02.537zM122 126.578a3.802 3.802 0 01-2.028-.559c-1.784-1.058-2.326-3.298-1.197-4.988l21.807-32.95-21.655-27.928c-1.25-1.617-.892-3.878.8-5.067 1.694-1.197 4.08-.849 5.33.76l23.188 29.907a3.462 3.462 0 01.16 4.082L125.21 124.88c-.709 1.102-1.944 1.697-3.21 1.697zM76.25 119.937a4.07 4.07 0 01-1.86-.457c-1.838-.979-2.494-3.183-1.465-4.959l30.18-51.359c1.03-1.755 3.34-2.385 5.2-1.37 1.83.978 2.486 3.175 1.457 4.937l-30.18 51.359c-.701 1.182-1.998 1.849-3.332 1.849zM22.875 18.125c0 2.003-1.706 3.625-3.813 3.625-2.106 0-3.812-1.622-3.812-3.625s1.706-3.625 3.813-3.625c2.106 0 3.812 1.622 3.812 3.625zM38.125 18.125c0 2.003-1.706 3.625-3.813 3.625-2.106 0-3.812-1.622-3.812-3.625s1.706-3.625 3.813-3.625c2.106 0 3.812 1.622 3.812 3.625zM53.375 18.125c0 2.003-1.706 3.625-3.813 3.625-2.106 0-3.812-1.622-3.812-3.625s1.706-3.625 3.813-3.625c2.106 0 3.812 1.622 3.812 3.625z"/></g><text id="documentElement.clie" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="12" y="101">documentElement.clientHeight</tspan></text><text id="documentElement.clie" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="257" y="195">documentElement.clientWidth</tspan></text><path id="Line-3-Copy" fill="#C06334" fill-rule="nonzero" d="M431.025 158.653l19 9.5-19 9.5-.001-8h-132v8l-19-9.5 19-9.5v8h132v-8z"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M256.5 45l9.5 19-8-.001v64l8 .001-9.5 19-9.5-19 8-.001v-64L247 64l9.5-19z"/></g></g></svg> \ No newline at end of file diff --git a/2-ui/1-document/10-size-and-scroll-window/document-client-width-height@2x.png b/2-ui/1-document/10-size-and-scroll-window/document-client-width-height@2x.png deleted file mode 100644 index 249db0edbe..0000000000 Binary files a/2-ui/1-document/10-size-and-scroll-window/document-client-width-height@2x.png and /dev/null differ diff --git a/2-ui/1-document/11-coordinates/article.md b/2-ui/1-document/11-coordinates/article.md index 9ec8f07902..0da4273062 100644 --- a/2-ui/1-document/11-coordinates/article.md +++ b/2-ui/1-document/11-coordinates/article.md @@ -49,7 +49,7 @@ function showRect(elem) { また: -- 座標は少数の場合があります。それは正常で、内部的にはブラウザは計算のためにそれを使用します。私たちは `style.position.left/top` へ設定するときにそれらを丸める必要はありません。ブラウザで少数は問題ありません。 +- 座標は小数の場合があります。それは正常で、内部的にはブラウザは計算のためにそれを使用します。私たちは `style.position.left/top` へ設定するときにそれらを丸める必要はありません。ブラウザで小数は問題ありません。 - 座標は負の値になる場合があります。例えば、ページが下にスクロールされ、`elem` の上端がウィンドウの上にある場合、`elem.getBoundingClientRect().top` は負の値になります。 - Chromeような一部のブラウザでは、結果 `getBoundingClientRect` にプロパティ `width` と `height` も追加します。減算することでもそれらを取得することは可能です: `height=bottom-top`, `width=right-left`。 @@ -152,7 +152,7 @@ setTimeout(() => message.remove(), 5000); 理由は明らかです: メッセージ要素は `position:fixed` に依存しているので、ページがスクロールしている間、ウィンドウの同じ場所にい続けます。 -変更するためには、ドキュメントベースの座標を使い、`position:absolute` をを使う必要があります。 +変更するためには、ドキュメントベースの座標を使い、`position:absolute` を使う必要があります。 ## ドキュメント座標 diff --git a/2-ui/1-document/11-coordinates/head.html b/2-ui/1-document/11-coordinates/head.html index 363a02b8ee..02ac715594 100644 --- a/2-ui/1-document/11-coordinates/head.html +++ b/2-ui/1-document/11-coordinates/head.html @@ -3,24 +3,27 @@ let elem = document.getElementById('coords-show-mark'); - elem && elem.onclick = function() { + // ebook (pdf/epub) 版では elem が無い + if (elem) { + elem.onclick = function() { - function createMessageUnder(elem, text) { - let coords = elem.getBoundingClientRect(); - let message = document.createElement('div'); - message.style.cssText = "position:fixed; color: red"; + function createMessageUnder(elem, text) { + let coords = elem.getBoundingClientRect(); + let message = document.createElement('div'); + message.style.cssText = "position:fixed; color: red"; - message.style.left = coords.left + "px"; - message.style.top = coords.bottom + "px"; + message.style.left = coords.left + "px"; + message.style.top = coords.bottom + "px"; - message.innerHTML = text; + message.innerHTML = text; - return message; - } + return message; + } - let message = createMessageUnder(elem, 'Hello, world!'); - document.body.append(message); - setTimeout(() => message.remove(), 5000); + let message = createMessageUnder(elem, 'Hello, world!'); + document.body.append(message); + setTimeout(() => message.remove(), 5000); + } } }); diff --git a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords.png b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords.png deleted file mode 100644 index 871acdc732..0000000000 Binary files a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords.png and /dev/null differ diff --git a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords.svg b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords.svg new file mode 100644 index 0000000000..fc26b023c5 --- /dev/null +++ b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="521" height="330" viewBox="0 0 521 330"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><filter id="filter-2" width="195.6%" height="153.8%" x="-42.9%" y="-25.8%" filterUnits="objectBoundingBox"><feMorphology in="SourceAlpha" operator="dilate" radius="1" result="shadowSpreadOuter1"/><feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1"/><feGaussianBlur in="shadowOffsetOuter1" result="shadowBlurOuter1" stdDeviation="1.5"/><feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"/><feColorMatrix in="shadowBlurOuter1" values="0 0 0 0 0.0941176471 0 0 0 0 0.0901960784 0 0 0 0 0.0901960784 0 0 0 1 0"/></filter><path id="path-1" d="M263 170v21.429l5.25-5.358 4.375 8.929h1.75s1.13-1.161.875-1.786c-1.203-2.947-4.375-8.928-4.375-8.928H277L263 170z"/></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="move-ball-coords.svg"><g id="noun_69008_cc" fill="#DBAF88" transform="translate(12 6)"><path id="Shape" d="M10.438 316C4.676 316 0 312.445 0 308.1V7.9C0 3.555 4.676 0 10.438 0h480.125C496.303 0 501 3.555 501 7.9v300.2c0 4.345-4.697 7.9-10.438 7.9H10.438zm-.276-12h478.643V12.939H10.162V304z"/><path id="Shape" d="M20.859 54.1c-5.753 0-10.422-1.147-10.422-2.55 0-1.402 4.67-2.55 10.422-2.55H479.4c5.732 0 10.422 1.148 10.422 2.55 0 1.403-4.69 2.55-10.422 2.55H20.86zM33.75 31.6c0 3.315-2.685 6-6 6s-6-2.685-6-6 2.685-6 6-6 6 2.685 6 6zM63.938 31.6c0 3.315-2.685 6-6 6s-6-2.685-6-6 2.685-6 6-6 6 2.685 6 6zM94.127 31.6c0 3.315-2.685 6-6 6s-6-2.685-6-6 2.685-6 6-6 6 2.685 6 6z"/></g><image id="Screen-Shot-2017-02-25-at-23.45.22" width="224" height="150" x="183" y="100" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAcAAAAEsCAYAAABUo2OKAAAABGdBTUEAALGOfPtRkwAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAABwKADAAQAAAABAAABLAAAAACEEXZQAAAS70lEQVR4Ae3cUU7rTBIGUDxiNzywIdbCKtgFe2ERILEB3uDB08woqLuRYsdxmfL1CbKwk3SlfartLz93NMNYHjceBAgQIEDgYAL/Odj5Ol0CBAgQIPA/AQFoIRAgQIDAIQUE4CHb7qQJECBA4LYnGIahf8oxAQIECBDYvUD/P3nxX4C7b6kTIECAAIElAgJwiZoxBAgQILB7AQG4+xY6AQIECBBYIiAAl6gZQ4AAAQK7FxCAu2+hEyBAgACBJQICcImaMQQIECCwewEBuPsWOgECBAgQWCIgAJeoGUOAAAECuxcQgLtvoRMgQIAAgSUCAnCJmjEECBAgsHsBAbj7FjoBAgQIEFgiIACXqBlDgAABArsXEIC7b6ETIECAAIElAgJwiZoxBAgQILB7AQG4+xY6AQIECBBYIiAAl6gZQ4AAAQK7FxCAu2+hEyBAgACBJQICcImaMQQIECCwewEBuPsWOgECBAgQWCJwu2TQRWPGi97tzQQIECBA4P8CQyyE/wKM9VWdAAECBJIKCMCkjTEtAgQIEIgVEICxvqoTIECAQFIBAZi0MaZFgAABArECAjDWV3UCBAgQSCogAJM2xrQIECBAIFZAAMb6qk6AAAECSQUEYNLGmBYBAgQIxAoIwFhf1QkQIEAgqYAATNoY0yJAgACBWAEBGOurOgECBAgkFRCASRtjWgQIECAQKyAAY31VJ0CAAIGkAgIwaWNMiwABAgRiBQRgrK/qBAgQIJBUQAAmbYxpESBAgECsgACM9VWdAAECBJIKCMCkjTEtAgQIEIgVEICxvqoTIECAQFIBAZi0MaZFgAABArECAjDWV3UCBAgQSCogAJM2xrQIECBAIFZAAMb6qk6AAAECSQVuN53XV/m0p7K9l+2hbPdlu+RhPD/rx/Xj/nHM++clWTH3vWP3KOPGVbex1Dv9PHa1P39eOb3j/G/j297wO79e+letH+unvr+5fvor5PzxX1w/db9W2O/ibtz2T6AvXSy/dcdTh8a3Qvxaj6kj66cVsn5aj6kj66cV2nr9tJ++zlGfiKVq+y3x2uP6O8VzV7t+bc6+8W1v5pjV7+HHr76e67UxZ9/6sX62Xj/1562w3+fd8P1EHaXDMNSH1+831Uu517J9lO1uYWnj+Vk/rh/3j2U30L3dP9eOozbubrYPwGVtM4oAAQIEjiYQHIDb/hvg0ZrnfAkQIEAgrYAATNsaEyNAgACBSAEBGKmrNgECBAikFRCAaVtjYgQIECAQKSAAI3XVJkCAAIG0AgIwbWtMjAABAgQiBQRgpK7aBAgQIJBWQACmbY2JESBAgECkgACM1FWbAAECBNIKCMC0rTExAgQIEIgUEICRumoTIECAQFoBAZi2NSZGgAABApECAjBSV20CBAgQSCsgANO2xsQIECBAIFJAAEbqqk2AAAECaQUEYNrWmBgBAgQIRAoIwEhdtQkQIEAgrYAATNsaEyNAgACBSAEBGKmrNgECBAikFRCAaVtjYgQIECAQKSAAI3XVJkCAAIG0AgIwbWtMjAABAgQiBQRgpK7aBAgQIJBWQACmbY2JESBAgECkgACM1FWbAAECBNIKCMC0rTExAgQIEIgUEICRumoTIECAQFoBAZi2NSZGgAABApECAjBSV20CBAgQSCsgANO2xsQIECBAIFLgNrL4r9pf5Zmnsr2X7aFs92W75GE8P+vH9eP+ccz75yVZMfe9Y/co48ZVt7HUO/08drU/f145veP8b+Pb3vA7v176V60f66e+v7l++ivk/PFfXD91v1bY7+Ju3PZPoC9dLL91x1OHxrdC/FqPqSPrpxWyflqPqSPrpxXaev20n77OUZ+IpWr7LfHa4/o7xXNXu35tzr7xbW/mmNXv4cevvp7rtTFn3/qxfrZeP/XnrbDf593w/UQdpcMw1IfX7zfVS7nXsn2U7W5haeP5WT+uH/ePZTfQvd0/146jNu5utg/AZW0zigABAgSOJhAcgNv+G+DRmud8CRAgQCCtgABM2xoTI0CAAIFIAQEYqas2AQIECKQVEIBpW2NiBAgQIBApIAAjddUmQIAAgbQCAjBta0yMAAECBCIFBGCkrtoECBAgkFZAAKZtjYkRIECAQKSAAIzUVZsAAQIE0goIwLStMTECBAgQiBQQgJG6ahMgQIBAWgEBmLY1JkaAAAECkQICMFJXbQIECBBIKyAA07bGxAgQIEAgUkAARuqqTYAAAQJpBQRg2taYGAECBAhECgjASF21CRAgQCCtgABM2xoTI0CAAIFIAQEYqas2AQIECKQVEIBpW2NiBAgQIBApIAAjddUmQIAAgbQCAjBta0yMAAECBCIFBGCkrtoECBAgkFZAAKZtjYkRIECAQKSAAIzUVZsAAQIE0goIwLStMTECBAgQiBQQgJG6ahMgQIBAWgEBmLY1JkaAAAECkQICMFJXbQIECBBIKyAA07bGxAgQIEAgUuA2sviv2l/lmaeyvZftoWz3ZbvkYTw/68f14/5xzPvnJVkx971j9yjjxlW3sdQ7/Tx2tT9/Xjm94/xv49ve8Du/XvpXrR/rp76/uX76K+T88V9cP3W/Vtjv4m7c9k+gL10sv3XHU4fGt0L8Wo+pI+unFbJ+Wo+pI+unFdp6/bSfvs5Rn4ilavst8drj+jvFc1e7fm3OvvFtb+aY1e/hx6++nuu1MWff+rF+tl4/9eetsN/n3fD9RB2lwzDUh9fvN9VLudeyfZTtbmFp4/lZP64f949lN9C93T/XjqM27m62D8BlbTOKAAECBI4mEByA2/4b4NGa53wJECBAIK2AAEzbGhMjQIAAgUgBARipqzYBAgQIpBUQgGlbY2IECBAgECkgACN11SZAgACBtAICMG1rTIwAAQIEIgUEYKSu2gQIECCQVkAApm2NiREgQIBApIAAjNRVmwABAgTSCgjAtK0xMQIECBCIFBCAkbpqEyBAgEBaAQGYtjUmRoAAAQKRAgIwUldtAgQIEEgrIADTtsbECBAgQCBSQABG6qpNgAABAmkFBGDa1pgYAQIECEQKCMBIXbUJECBAIK2AAEzbGhMjQIAAgUgBARipqzYBAgQIpBUQgGlbY2IECBAgECkgACN11SZAgACBtAICMG1rTIwAAQIEIgUEYKSu2gQIECCQVkAApm2NiREgQIBApIAAjNRVmwABAgTSCgjAtK0xMQIECBCIFBCAkbpqEyBAgEBaAQGYtjUmRoAAAQKRAgIwUldtAgQIEEgrIADTtsbECBAgQCBS4Day+K/aX+WZp7K9l+2hbPdlu+RhPD/rx/Xj/nHM++clWTH3vWP3KOPGVbex1Dv9PHa1P39eOb3j/G/j297wO79e+letH+unvr+5fvor5PzxX1w/db9W2O/ibtz2T6AvXSy/dcdTh8a3Qvxaj6kj66cVsn5aj6kj66cV2nr9tJ++zlGfiKVq+y3x2uP6O8VzV7t+bc6+8W1v5pjV7+HHr76e67UxZ9/6sX62Xj/1562w3+fd8P1EHaXDMNSH1+831Uu517J9lO1uYWnj+Vk/rh/3j2U30L3dP9eOozbubrYPwGVtM4oAAQIEjiYQHIDb/hvg0ZrnfAkQIEAgrYAATNsaEyNAgACBSAEBGKmrNgECBAikFRCAaVtjYgQIECAQKSAAI3XVJkCAAIG0AgIwbWtMjAABAgQiBQRgpK7aBAgQIJBWQACmbY2JESBAgECkgACM1FWbAAECBNIKCMC0rTExAgQIEIgUEICRumoTIECAQFoBAZi2NSZGgAABApECAjBSV20CBAgQSCsgANO2xsQIECBAIFJAAEbqqk2AAAECaQUEYNrWmBgBAgQIRAoIwEhdtQkQIEAgrYAATNsaEyNAgACBSAEBGKmrNgECBAikFRCAaVtjYgQIECAQKSAAI3XVJkCAAIG0AgIwbWtMjAABAgQiBQRgpK7aBAgQIJBWQACmbY2JESBAgECkgACM1FWbAAECBNIKCMC0rTExAgQIEIgUEICRumoTIECAQFoBAZi2NSZGgAABApECAjBSV20CBAgQSCsgANO2xsQIECBAIFLgNrL4r9pf5Zmnsr2X7aFs92W75GE8P+vH9eP+ccz75yVZMfe9Y/co48ZVt7HUO/08drU/f145veP8b+Pb3vA7v176V60f66e+v7l++ivk/PFfXD91v1bY7+Ju3PZPoC9dLL91x1OHxrdC/FqPqSPrpxWyflqPqSPrpxXaev20n77OUZ+IpWr7LfHa4/o7xXNXu35tzr7xbW/mmNXv4cevvp7rtTFn3/qxfrZeP/XnrbDf593w/UQdpcMw1IfX7zfVS7nXsn2U7W5haeP5WT+uH/ePZTfQvd0/146jNu5utg/AZW0zigABAgSOJhAcgNv+G+DRmud8CRAgQCCtgABM2xoTI0CAAIFIAQEYqas2AQIECKQVEIBpW2NiBAgQIBApIAAjddUmQIAAgbQCAjBta0yMAAECBCIFBGCkrtoECBAgkFZAAKZtjYkRIECAQKSAAIzUVZsAAQIE0goIwLStMTECBAgQiBQQgJG6ahMgQIBAWgEBmLY1JkaAAAECkQICMFJXbQIECBBIKyAA07bGxAgQIEAgUkAARuqqTYAAAQJpBQRg2taYGAECBAhECgjASF21CRAgQCCtgABM2xoTI0CAAIFIAQEYqas2AQIECKQVEIBpW2NiBAgQIBApIAAjddUmQIAAgbQCAjBta0yMAAECBCIFBGCkrtoECBAgkFZAAKZtjYkRIECAQKSAAIzUVZsAAQIE0goIwLStMTECBAgQiBQQgJG6ahMgQIBAWgEBmLY1JkaAAAECkQICMFJXbQIECBBIKyAA07bGxAgQIEAgUuA2sviv2l/lmaeyvZftoWz3ZbvkYTw/68f14/5xzPvnJVkx971j9yjjxlW3sdQ7/Tx2tT9/Xjm94/xv49ve8Du/XvpXrR/rp76/uX76K+T88V9cP3W/Vtjv4m7c9k+gL10sv3XHU4fGt0L8Wo+pI+unFbJ+Wo+pI+unFdp6/bSfvs5Rn4ilavst8drj+jvFc1e7fm3OvvFtb+aY1e/hx6++nuu1MWff+rF+tl4/9eetsN/n3fD9RB2lwzDUh9fvN9VLudeyfZTtbmFp4/lZP64f949lN9C93T/XjqM27m62D8BlbTOKAAECBI4mEByA2/4b4NGa53wJECBAIK2AAEzbGhMjQIAAgUgBARipqzYBAgQIpBUQgGlbY2IECBAgECkgACN11SZAgACBtAICMG1rTIwAAQIEIgUEYKSu2gQIECCQVkAApm2NiREgQIBApIAAjNRVmwABAgTSCgjAtK0xMQIECBCIFBCAkbpqEyBAgEBaAQGYtjUmRoAAAQKRAgIwUldtAgQIEEgrIADTtsbECBAgQCBSQABG6qpNgAABAmkFBGDa1pgYAQIECEQKCMBIXbUJECBAIK2AAEzbGhMjQIAAgUgBARipqzYBAgQIpBUQgGlbY2IECBAgECkgACN11SZAgACBtAICMG1rTIwAAQIEIgUEYKSu2gQIECCQVkAApm2NiREgQIBApIAAjNRVmwABAgTSCgjAtK0xMQIECBCIFBCAkbpqEyBAgEBaAQGYtjUmRoAAAQKRAgIwUldtAgQIEEgrIADTtsbECBAgQCBS4Day+K/aX+WZp7K9l+2hbPdlu+RhPD/rx/Xj/nHM++clWTH3vWP3KOPGVbex1Dv9PHa1P39eOb3j/G/j297wO79e+letH+unvr+5fvor5PzxX1w/db9W2O/ibtz2T6AvXSy/dcdTh8a3Qvxaj6kj66cVsn5aj6kj66cV2nr9tJ++zlGfiKVq+y3x2uP6O8VzV7t+bc6+8W1v5pjV7+HHr76e67UxZ9/6sX62Xj/1562w3+fd8P1EHaXDMNSH1+831Uu517J9lO1uYWnj+Vk/rh/3j2U30L3dP9eOozbubrYPwGVtM4oAAQIEjiYQHIDb/hvg0ZrnfAkQIEAgrYAATNsaEyNAgACBSAEBGKmrNgECBAikFRCAaVtjYgQIECAQKSAAI3XVJkCAAIG0AgIwbWtMjAABAgQiBeL/v0BX/p+xRmKoTYAAAQLHEfBfgMfptTMlQIAAgUpAAFYYdgkQIEDgOAIC8Di9dqYECBAgUAkIwArDLgECBAgcR0AAHqfXzpQAAQIEKgEBWGHYJUCAAIHjCAjA4/TamRIgQIBAJSAAKwy7BAgQIHAcAQF4nF47UwIECBCoBARghWGXAAECBI4jIACP02tnSoAAAQKVgACsMOwSIECAwHEEBOBxeu1MCRAgQKASEIAVhl0CBAgQOI6AADxOr50pAQIECFQCArDCsEuAAAECxxEQgMfptTMlQIAAgUpAAFYYdgkQIEDgOAK3/akOw9A/5ZgAAQIECPxzAsNYHv/cWTkhAgQIECAwIeBPoBNAXiZAgACBf1NAAP6bfXVWBAgQIDAhIAAngLxMgAABAv+mwH8BIoXpK3F6REIAAAAASUVORK5CYII="/><text id="ball.style.left" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="174" y="262">ball.style.left</tspan></text><text id="?" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="40" font-weight="normal"><tspan x="217.5" y="229">?</tspan></text><text id="fieldCoords.left" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="48.9" y="189.32">fieldCoords.left</tspan></text><text id="event.clientX" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="12" font-weight="normal"><tspan x="59.7" y="161.32">event.clientX</tspan></text><path id="Line-41" fill="#C06334" fill-rule="nonzero" d="M168.41 187.91l14 7-14 7v-6H39.589l.001 6-14-7 14-7-.001 6H168.41v-6z"/><path id="Line-41-Copy-2" fill="#C06334" fill-rule="nonzero" d="M248 160.91l14 7-14 7v-6H39.589l.001 6-14-7 14-7-.001 6H248v-6z"/><g id="ball" opacity=".7" transform="translate(261 168)"><circle id="Oval-4" cx="18.753" cy="18.753" r="18.753" fill="#FFF"/><path id="Shape" fill="#181717" fill-rule="nonzero" d="M37.792 18.204a.229.229 0 00-.003-.123l-.004-.016a18.9 18.9 0 00-6.003-12.94c-.445-.461-1.107-1.006-1.863-1.534A19.043 19.043 0 0012.899.935c-.412.106-.98.322-1.64.622a18.593 18.593 0 00-3.53 2.014A18.856 18.856 0 004.68 6.344c-6.874 7.794-6.085 19.762 1.758 26.679a19.043 19.043 0 004.876 3.128c.254.121.465.194.645.272 7.417 2.964 15.919.96 21.169-4.992 3.216-3.65 4.87-8.343 4.663-13.227zm-.677-.785l-.05-.135c-.387-1.04-1.095-2.957-2.828-4.492-.255-3.228-1.405-5.115-2.43-6.774a18.297 18.297 0 015.308 11.401zM24.485 3.158a.476.476 0 00.044-.013c1.666.291 4.245 1.237 6.464 2.748a.274.274 0 00.03.068c.06.1.214.347.214.347.99 1.6 2.109 3.414 2.362 6.494-1.007.741-1.724 1.531-2.42 2.295-.414.456-.84.925-1.326 1.383-1.63-1.516-4.52-2.813-7.641-3.42-.306-1.35-.919-3.854-2.423-6.628l.116-.132c2.418-2.743 3.269-2.9 4.255-3.08.105-.019.213-.039.325-.062zm-8.729 15.517l6.255-5.001c3.137.597 6.025 1.903 7.547 3.413.372 1.153.468 3.636.54 5.464.035.935.069 1.754.127 2.246-2.457 2.24-5.717 4.539-6.78 4.778l-6.843-2.677c-.512-2.867-.662-4.83-.846-8.223zm.205 8.273l-4.955 3.485a.249.249 0 00-.148-.012c-.923.192-4.479-2.415-6.21-3.929a.264.264 0 00-.065-.04c-.073-.495-.23-1.125-.408-1.84-.469-1.885-1.05-4.221-.528-5.673l.028-.028c.006-.007.013-.015.018-.024.339-.487.665-.908 1-1.286 1.058-1.2 2.256-2.053 4.12-2.927l.379.276c1.735 1.266 4.094 2.99 5.927 3.8.185 3.374.335 5.339.842 8.198zM9.35 3.678c.07-.079.145-.147.233-.226l.049-.044c.185-.102.424-.243.702-.405.653-.381 1.546-.905 2.527-1.398a18.483 18.483 0 018.294-.832c0 .093.052.18.137.223.17.089.343.172.515.255.733.354 1.432.692 2.073 1.37-1.001.19-2.025.496-4.464 3.262l-.156.177c-2.457-.245-5.282-.38-7.822.37-.94-1.119-1.73-1.651-2.429-2.123l-.019-.013c.153-.357.266-.51.36-.616zM1.413 15.43a.25.25 0 00-.398-.123 1.375 1.375 0 00-.076.066 18.024 18.024 0 014.08-8.465c.12-.1.274-.233.448-.385.724-.63 2.41-2.101 2.946-1.847a13.536 13.536 0 00.092.063l.147.1c.652.44 1.39.937 2.258 1.96-.78 1.657-2.416 5.752-2.408 7.314-1.927.907-3.177 1.802-4.288 3.062-.295.334-.584.699-.878 1.108l-.106-.1c-.557-.52-1.396-1.306-1.817-2.753zm5.438 17.124a18.323 18.323 0 01-3.95-4.92c.691-.195 1.023-.323 1.411-.586.638.539 4.586 3.827 6.334 4.006a36.692 36.692 0 003.055 4.295c-.572.352-1.042.473-1.648.437a18.311 18.311 0 01-5.202-3.232zm6.469 3.698c.295-.108.593-.268.92-.49 2.756.407 5.828.152 7.99-.152.031.021.066.034.105.04.52.068 2.062.226 3.92.015a18.292 18.292 0 01-12.935.587zm18.778-4.768a.256.256 0 00-.042.181 17.964 17.964 0 01-3.83 3.004.236.236 0 00-.111.003c-2.244.59-4.392.479-5.354.382.433-1.078.73-2.68.902-4.876 1.262-.352 4.456-2.594 7.01-4.925.636.105 1.07.204 1.421.286.858.198 1.25.29 2.81.086-.201 1.17-.765 2.896-2.806 5.86zm2.797-3.623c.438-1.046.605-1.827.69-2.448a.237.237 0 00-.014-.125c.39-.886 1.186-3.392 1.594-5.443a18.028 18.028 0 01-2.27 8.016z"/></g><g id="Default"><use fill="#000" filter="url(#filter-2)" xlink:href="#path-1"/><path fill="#FFF" stroke="#A7333A" d="M262.5 168.775l15.69 16.01h-6.488c.886 1.695 3.06 5.91 4.01 8.24.318.776-.979 2.324-.979 2.324h0l-2.42.151-4.2-8.574-5.613 5.727v-23.878z"/></g><path id="Line-16-Copy-2" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M195.535 180.945s20.728 10.637 32.233 10.637c11.504 0 32.232-10.637 32.232-10.637"/></g></g></svg> \ No newline at end of file diff --git a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords@2x.png b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords@2x.png deleted file mode 100644 index 923bfc822e..0000000000 Binary files a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/move-ball-coords@2x.png and /dev/null differ diff --git a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.md b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.md index 70d2da4fe7..57e9b9f869 100644 --- a/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.md +++ b/2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.md @@ -27,7 +27,7 @@ これはその図です: -![](move-ball-coords.png) +![](move-ball-coords.svg) 私たちは `event.clientX/clientY` -- クリックのウィンドウに相対的な座標 -- を持っています。 @@ -47,6 +47,6 @@ let left = event.clientX - fieldInnerCoords.left - field.clientLeft; let left = event.clientX - fieldInnerCoords.left - field.clientLeft - ball.offsetWidth/2; ``` -縦の座標はは同じロジックを使って計算します。 +縦の座標は同じロジックを使って計算します。 ボールの幅/高さは、`ball.offsetWidth` にアクセスしたときに知っていなければならないことに注意してください。HTMLまたはCSSで指定する必要があります。 diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1.png b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1.png deleted file mode 100644 index 22a8ab6bec..0000000000 Binary files a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1.png and /dev/null differ diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1.svg b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1.svg new file mode 100644 index 0000000000..5bb161f6cb --- /dev/null +++ b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="488" height="246" viewBox="0 0 488 246"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="combined" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="carousel1.svg"><rect id="Rectangle-18" width="63" height="63" x="41.5" y="98.5" fill="#FBF2EC" stroke="#AF6E24" stroke-width="3" rx="10"/><rect id="Rectangle-19" width="63" height="63" x="109.5" y="98.5" fill="#FBF2EC" stroke="#AF6E24" stroke-width="3" rx="10"/><rect id="Rectangle-20" width="63" height="63" x="177.5" y="98.5" fill="#FBF2EC" stroke="#AF6E24" stroke-width="3" rx="10"/><rect id="Rectangle-21" width="63" height="63" x="245.5" y="98.5" fill="#FBF2EC" stroke="#AF6E24" stroke-width="3" opacity=".5" rx="10"/><rect id="Rectangle-22" width="63" height="63" x="313.5" y="98.5" fill="#FBF2EC" stroke="#AF6E24" stroke-width="3" opacity=".5" rx="10"/><rect id="Rectangle-23" width="63" height="63" x="381.5" y="98.5" fill="#FBF2EC" stroke="#AF6E24" stroke-width="3" opacity=".5" rx="10"/><text id="…-2" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="24" font-weight="normal"><tspan x="406" y="131">…</tspan></text><path id="Rectangle-24" stroke="#C06334" stroke-width="2" d="M38 95h412v70H38z"/><path id="Rectangle-1" fill="#DBAF88" d="M261 42v171H19V42h242zm-17 17H36v137h208V59z"/><text id="div-(container)" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="82.5" y="32">div (container)</tspan></text><text id="130x130" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="13" font-weight="normal"><tspan x="46" y="133">130x130</tspan></text><text id="ul-(width:-9999px)" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="71.5" y="82">ul (width: 9999px)</tspan></text><path id="Rectangle-36" fill="#FFF" d="M447 90h16v84h-16z"/></g></g></svg> \ No newline at end of file diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1@2x.png b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1@2x.png deleted file mode 100644 index 9d2ff7e532..0000000000 Binary files a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel1@2x.png and /dev/null differ diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2.png b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2.png deleted file mode 100644 index bb2c6219f8..0000000000 Binary files a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2.png and /dev/null differ diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2.svg b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2.svg new file mode 100644 index 0000000000..81aea5b89e --- /dev/null +++ b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="639" height="246" viewBox="0 0 639 246"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="carousel2.svg"><rect id="Rectangle-18" width="63" height="63" x="41.5" y="98.5" fill="#FBF2EC" stroke="#AF6E24" stroke-width="3" opacity=".5" rx="10"/><rect id="Rectangle-19" width="63" height="63" x="109.5" y="98.5" fill="#FBF2EC" stroke="#AF6E24" stroke-width="3" opacity=".5" rx="10"/><rect id="Rectangle-20" width="63" height="63" x="177.5" y="98.5" fill="#FBF2EC" stroke="#AF6E24" stroke-width="3" opacity=".5" rx="10"/><rect id="Rectangle-21" width="63" height="63" x="245.5" y="98.5" fill="#FBF2EC" stroke="#AF6E24" stroke-width="3" rx="10"/><rect id="Rectangle-22" width="63" height="63" x="313.5" y="98.5" fill="#FBF2EC" stroke="#AF6E24" stroke-width="3" rx="10"/><rect id="Rectangle-23" width="63" height="63" x="381.5" y="98.5" fill="#FBF2EC" stroke="#AF6E24" stroke-width="3" rx="10"/><rect id="Rectangle-26" width="63" height="63" x="449.5" y="98.5" fill="#FBF2EC" stroke="#AF6E24" stroke-width="3" opacity=".5" rx="10"/><rect id="Rectangle-25" width="63" height="63" x="517.5" y="98.5" fill="#FBF2EC" stroke="#AF6E24" stroke-width="3" opacity=".5" rx="10"/><text id="…-3" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="24" font-weight="normal"><tspan x="542" y="131">…</tspan></text><path id="Rectangle-24" stroke="#C06334" stroke-width="2" d="M38 95h546v70H38z"/><path id="Rectangle-1" fill="#DBAF88" d="M467 42v171H225V42h242zm-17 17H242v137h208V59z"/><text id="div-(container)" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="288.5" y="32">div (container)</tspan></text><text id="130x130" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="13" font-weight="normal"><tspan x="252" y="133">130x130</tspan></text><text id="ul-(margin-left:--35" fill="#C06334" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="246.5" y="82">ul (margin-left: -350px)</tspan></text><path id="Rectangle-36" fill="#FFF" d="M582 83h16v84h-16z"/></g></g></svg> \ No newline at end of file diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2@2x.png b/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2@2x.png deleted file mode 100644 index 37aa58cc68..0000000000 Binary files a/2-ui/2-events/01-introduction-browser-events/07-carousel/carousel2@2x.png and /dev/null differ diff --git a/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.md b/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.md index 4debc4b32f..19ba7e1356 100644 --- a/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.md +++ b/2-ui/2-events/01-introduction-browser-events/07-carousel/solution.md @@ -2,7 +2,7 @@ 通常、このようなリボンは幅広ですが、リボンの一部のみが見えるよう、固定サイズの `<div>` を置いて、それを "切り取る" ようにします: -![](carousel1.png) +![](carousel1.svg) リストを水平に表示するために、`display: inline-block` のような `<li>` に対して正しい CSS プロパティを適用する必要があります。 @@ -10,7 +10,7 @@ スクロールするために、`<ul>` をシフトします。そのための方法はたくさんありますが、例えば `margin-left` を変更するか `transform: translateX()` (より良いパフォーマンスです)を使います。: -![](carousel2.png) +![](carousel2.svg) 外部の `<div>` は固定幅なので、"余分な" イメージはカットされます。 diff --git a/2-ui/2-events/02-bubbling-and-capturing/article.md b/2-ui/2-events/02-bubbling-and-capturing/article.md index ab2a79e19c..b4183cce34 100644 --- a/2-ui/2-events/02-bubbling-and-capturing/article.md +++ b/2-ui/2-events/02-bubbling-and-capturing/article.md @@ -1,6 +1,6 @@ # バブリング と キャプチャリング -例で始めてみましょう。 +例から始めましょう。 このハンドラは `<div>` へ割り当てられますが、`<em>` や `<code>` のような任意のネストされたタグをクリックしたときにも実行されます: @@ -10,15 +10,15 @@ </div> ``` -少し奇妙に見えますよね? なぜ実際のクリックが `<em>` だった場合に `<div>` 上のハンドラが実行されるでしょう? +不思議ですよね?実際にクリックしたのは `<em>` でしたが、 `<div>` のハンドラが実行されるのはなぜでしょう? ## バブリング(Bubbling) バブリングの原理はシンプルです。 -**要素上でイベントが起きると、最初にその上のハンドラが実行され、次にその親のハンドラが実行され、他の祖先に到達するまでそれらが行われます。** +**要素上でイベントが発生すると、最初にその要素上のハンドラが実行され、次にその親要素のハンドラが実行され、さらに他の祖先を実行します。** -たとえば、3つのネストされた要素 `FORM > DIV > P` があり、それぞれにハンドラがあります: +たとえば、3つのネストされた要素 `FORM > DIV > P` があり、それぞれにハンドラがあるとします: ```html run autorun <style> @@ -35,47 +35,47 @@ </form> ``` -`<p>` の内部のクリックでは、最初に `onclick` を実行します: -1. その `<p>`. -2. 次に外部の `<div>`. -3. 次に外部の `<form>`. +`<p>` をクリックすると、最初に `onclick` を実行します: +1. クリックした `<p>`. +2. 次に外側の `<div>`. +3. 次に外側の `<form>`. 4. そして、`document` オブジェクトまで登ります. -![](event-order-bubbling.png) +![](event-order-bubbling.svg) なので、`<p>` をクリックすると、3つのアラートが表示されます: `p` -> `div` -> `form` -このプロセスは "バブリング" と呼ばれます。なぜなら、水の中の泡のように、イベントが内部の要素から親に至るまで "バブル" しているためです。 +このプロセスは "バブリング" と呼ばれます。なぜなら、水の中の泡のようにイベントが内部の要素から親に至るまで "バブル" しているためです。 ```warn header="*ほぼ* すべてのイベントがバブルします" このフレーズのキーワードは、"ほとんど" です。 -例えば、`focus` イベントはバブルしません。他にも例があり、私たちはそれらを見ていくでしょう。しかし、それはルールというよりはむしろ例外であり、ほとんどのイベントはバブルします。 +例えば、`focus` イベントはバブルしません。他にも例はあります。ですが、それはルールというよりはむしろ例外であり、ほとんどのイベントはバブルします。 ``` ## event.target 親要素のハンドラは、常に実際に発生した場所についての詳細を取得できます。 -**イベントを起こした最も深くネストされた要素は *ターゲット* 要素と呼ばれ、 `event.target` でアクセス可能です。** +**イベントが発生した最も深くネストされた要素は *ターゲット* 要素と呼ばれ、 `event.target` でアクセスできます。** -`this` (=`event.currentTarget`) との違いは次です: +`this` (=`event.currentTarget`) との違いは以下の通りです: -- `event.target` -- はイベントを始めた "ターゲット" 要素で、バブリングプロセスを通して変化しません。 +- `event.target` -- はイベントが開始された "ターゲット" 要素で、バブリングプロセスを通して変化しません。 - `this` -- は "現在" の要素で、現在実行中のハンドラを持ちます。 -例えば、単一のハンドラ `form.onclick` を持っている場合、その form 中のすべてのクリックを "キャッチ" できます。どこでクリックされたかは関係なく、`<form>` までバブルし、そのハンドラを実行します。 +例えば、単一のハンドラ `form.onclick` がある場合、その form 中のすべてのクリックを "キャッチ" できます。どこでクリックされたかは関係なく、`<form>` までバブルし、そのハンドラを実行します。 -`form.onclick` ハンドラの中は: +`form.onclick` ハンドラの中: -- `this` (`=event.currentTarget`) は `<form>` 要素です。なぜなら、ハンドラはそこで動いているからです。 +- `this` (`=event.currentTarget`) は `<form>` 要素です。ハンドラはそこで動いているからです。 - `event.target` は実際にクリックされた form の内側にある具体的な要素です。 見てみましょう: [codetabs height=220 src="bubble-target"] -It's possible that `event.target` equals `this` -- when the click is made directly on the `<form>` element. +`event.target` と `this` が等しい場合があります。クリックを `<form>` 要素で直接行った場合です。 ## バブリングを止める @@ -83,7 +83,7 @@ It's possible that `event.target` equals `this` -- when the click is made direct しかし、どのハンドラも、イベントが完全に処理されたと判断し、バブリングを止めることができます。 -そのためのメソッドは `event.stopPropagation()` です. +そのためのメソッドは `event.stopPropagation()` です。 例えば、ここで `<button>` をクリックしても `body.onclick` は動作しません。 @@ -128,7 +128,7 @@ It's possible that `event.target` equals `this` -- when the click is made direct これは、テーブル中の `<td>` をクリックしたときの図で、仕様から抜粋したものです: -![](eventflow.png) +![](eventflow.svg) つまり: `<td>` をクリックした場合、イベントは最初に祖先のチェーンを通って要素へ下りていき(キャプチャリング)、ターゲットに到達した後、ハンドラを呼び出しながら上に行き(バブル)ます。 @@ -138,16 +138,21 @@ It's possible that `event.target` equals `this` -- when the click is made direct キャプチャリングフェーズでイベントをキャッチするには、`addEventListener` の3つ目の引数を `true` にする必要があります。 +```js +elem.addEventListener(..., {capture: true}) +// または, 単に "true" は {capture: true} のエイリアスです +elem.addEventListener(..., true) +``` + 最後の引数は2つのとり得る値があります: - `false` (デフォルト) の場合、ハンドラはバブリングフェーズで設定されます。 - `true` の場合、ハンドラはキャプチャリングフェーズで設定されます。 -正式には3つのフェーズがありますが、2つ目のフェーズ("ターゲットフェーズ": イベントが要素に到達した)は個別に処理されないことに注意してください: キャプチャフェーズとバブリングフェーズの両方のハンドラがそのフェーズでトリガします。 -キャプチャリングとバブリングハンドラをターゲット要素に置くと、キャプチャハンドラはキャプチャフェーズの最後にトリガし、バブルハンドラはバブリングフェーズで最初にトリガします。 +正式には3つのフェーズがありますが、2つ目のフェーズ("ターゲットフェーズ": イベントが要素に到達した)は個別に処理されないことに注意してください: キャプチャフェーズとバブリングフェーズの両方のハンドラがそのフェーズでトリガします。 -動作を見てみましょう: +キャプチャリングとバブリング両方の例を見てみましょう: ```html run autorun height=140 edit <style> @@ -173,14 +178,26 @@ It's possible that `event.target` equals `this` -- when the click is made direct どのハンドラが動作するかを見るために、ドキュメント上の *すべての* 要素にクリックハンドラを設定しています。 -`<p>` をクリックすると、シーケンスは次の通りです: +`<p>` をクリックしたときの処理の流れは次の通りです: -1. `HTML` -> `BODY` -> `FORM` -> `DIV` -> `P` (キャプチャリングフェーズ, 1つ目のリスナーです)そして、: -2. `P` -> `DIV` -> `FORM` -> `BODY` -> `HTML` (バブリングフェーズ, 2つ目のリスナーです). +1. `HTML` -> `BODY` -> `FORM` -> `DIV` (キャプチャリングフェーズ, 1つ目のリスナーです): +2. `P` (ターゲットフェーズ, キャプチャリングとバブリング2つのリスナをセットしているので、2度実行されます) +3. `DIV` -> `FORM` -> `BODY` -> `HTML` (バブリングフェーズ, 2つ目のリスナーです). -`P` が2回表示されることに注意してください: キャプチャリングの終わりと、バブリングの開始です。 +イベントが捕捉されたフェーズの番号を示すプロパティ `event.eventPhase` があります。ですが、通常はハンドラでそれを知っているので、めったに使用されません。 -イベントが捕捉されたフェーズの番号を示すプロパティ `event.eventPhase` があります。 しかし、私たちは通常ハンドラでそれを知っているので、めったに使用されません。 +```smart header="To remove the handler, `removeEventListener` needs the same phase" +If we `addEventListener(..., true)`, then we should mention the same phase in `removeEventListener(..., true)` to correctly remove the handler. +``` + +````smart header="Listeners on same element and same phase run in their set order" +If we have multiple event handlers on the same phase, assigned to the same element with `addEventListener`, they run in the same order as they are created: + +```js +elem.addEventListener("click", e => alert(1)); // guaranteed to trigger first +elem.addEventListener("click", e => alert(2)); +``` +```` ## サマリ @@ -188,7 +205,7 @@ It's possible that `event.target` equals `this` -- when the click is made direct - イベントが発生したとき -- それが起きた最もネストされた要素は "ターゲット要素" (`event.target`) としてラベル付けされます。 - 次にイベントは `addEventListener(...., true)` で割り当てられたハンドラを呼び出しながらドキュメントルートから `event.target` へ下りていきます。 -- その後、イベントは `on<event>` と3つ目の引数ががないもしくは `false` の `addEventListener` を使って割り当てられたハンドラを実行しながら `event.target` からルートまで上がっていきます。 +- その後、イベントは `on<event>` と3つ目の引数がないもしくは `false` の `addEventListener` を使って割り当てられたハンドラを実行しながら `event.target` からルートまで上がっていきます。 それぞれのハンドラは `event` オブジェクトのプロパティにアクセスできます: diff --git a/2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/index.html b/2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/index.html index 8313ec29fe..19a89668ee 100644 --- a/2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/index.html +++ b/2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/index.html @@ -7,7 +7,7 @@ </head> <body> - A click shows both <code>event.target</code> and <code>this</code> to compare: + クリックすると、<code>event.target</code> と <code>this</code> が表示されます。比較してみましょう。 <form id="form">FORM <div>DIV diff --git a/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling.png b/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling.png deleted file mode 100644 index a4f4fae511..0000000000 Binary files a/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling.png and /dev/null differ diff --git a/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling.svg b/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling.svg new file mode 100644 index 0000000000..b62d990890 --- /dev/null +++ b/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="353" height="216" viewBox="0 0 353 216"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="event-order-bubbling.svg"><path id="Rectangle-210" fill="#DBAF88" d="M159.488 140L174 186H60l14.512-46z"/><path id="Rectangle-209" stroke="#91C2A3" stroke-width="18" d="M173.634 81l16.09 51H43.276l16.09-51h114.267z"/><path id="Rectangle-208" stroke="#EFA39F" stroke-width="18" d="M39.986 29h153.028l22.71 72H17.276l22.71-72z"/><path id="Fill-46" fill="#166388" d="M121.5 141v13.816a4.5 4.5 0 11-9 0V141h9zm0-31v13h-9v-13h9zM117 20.53a4.471 4.471 0 013.362 1.3l17.64 17.64a4.5 4.5 0 01-6.364 6.364L121.5 35.698V92h-9V35.7l-10.138 10.136a4.5 4.5 0 11-6.363-6.364l17.639-17.64a4.477 4.477 0 013.363-1.3z"/><text id="1" fill="#643B0C" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="210" y="105">1</tspan></text><text id="2" fill="#643B0C" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="185" y="136">2</tspan></text><text id="3" fill="#C06334" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="157" y="181">3</tspan></text><text id="Most-deeply-nested-e" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="233.48" y="175">Most deeply</tspan> <tspan x="221.306" y="194">nested element</tspan></text><path id="Line-30" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M179.5 177.5h30"/></g></g></svg> \ No newline at end of file diff --git a/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling@2x.png b/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling@2x.png deleted file mode 100644 index e3a070cb80..0000000000 Binary files a/2-ui/2-events/02-bubbling-and-capturing/event-order-bubbling@2x.png and /dev/null differ diff --git a/2-ui/2-events/02-bubbling-and-capturing/eventflow.png b/2-ui/2-events/02-bubbling-and-capturing/eventflow.png deleted file mode 100644 index 95c5c08e0a..0000000000 Binary files a/2-ui/2-events/02-bubbling-and-capturing/eventflow.png and /dev/null differ diff --git a/2-ui/2-events/02-bubbling-and-capturing/eventflow.svg b/2-ui/2-events/02-bubbling-and-capturing/eventflow.svg new file mode 100644 index 0000000000..566064cd6b --- /dev/null +++ b/2-ui/2-events/02-bubbling-and-capturing/eventflow.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="641" height="633" viewBox="0 0 641 633"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="eventflow" transform="translate(22 28)"><g id="nodes"><g id="Group" transform="translate(224.685)"><path id="Rectangle-path" fill="#D1CFCD" stroke="#181717" d="M0 0h134.427v34.567H0z"/><text id="Window" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="15.555" font-weight="normal"><tspan x="39.368" y="21.913">Window</tspan></text></g><g id="Group" transform="translate(234.287 60.493)"><path id="Rectangle-path" fill="#D1CFCD" stroke="#181717" d="M0 0h115.223v34.567H0z"/><text id="Document" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="15.555" font-weight="normal"><tspan x="22.084" y="21.913">Document</tspan></text></g><g id="html-node" transform="translate(243.889 120.985)"><rect id="Rectangle-path" width="96.019" height="34.567" x="0" y="0" fill="#C9DCEA" stroke="#181717" rx="5"/><text id="<html>" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="15.555" font-weight="normal"><tspan x="23.045" y="21.913"><html></tspan></text></g><g id="body-node" transform="translate(243.889 181.478)"><rect id="Rectangle-path" width="96.019" height="34.567" x="0" y="0" fill="#C9DCEA" stroke="#181717" rx="5"/><text id="<body>" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="15.555" font-weight="normal"><tspan x="23.045" y="21.913"><body></tspan></text></g><g id="table-node" transform="translate(243.889 241.97)"><rect id="Rectangle-path" width="96.019" height="34.567" x="0" y="0" fill="#C9DCEA" stroke="#181717" rx="5"/><text id="<table>" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="15.555" font-weight="normal"><tspan x="23.045" y="21.913"><table></tspan></text></g><g id="tbody-node" transform="translate(243.889 302.463)"><rect id="Rectangle-path" width="96.019" height="34.567" x="0" y="0" fill="#C9DCEA" stroke="#181717" rx="5"/><text id="<tbody>" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="15.555" font-weight="normal"><tspan x="19.204" y="21.913"><tbody></tspan></text></g><g id="tr_1-node" transform="translate(80.656 380.239)"><rect id="Rectangle-path" width="96.019" height="34.567" x="0" y="0" fill="#C9DCEA" stroke="#181717" rx="5"/><text id="<tr>" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="15.555" font-weight="normal"><tspan x="35.527" y="21.913"><tr></tspan></text></g><g id="tr_2-node" transform="translate(426.325 380.239)"><rect id="Rectangle-path" width="96.019" height="34.567" x="0" y="0" fill="#C9DCEA" stroke="#181717" rx="5"/><text id="<tr>" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="15.555" font-weight="normal"><tspan x="33.607" y="21.913"><tr></tspan></text></g><g id="tr_1_td_1-node" transform="translate(13.443 458.015)"><rect id="Rectangle-path" width="96.019" height="34.567" x="0" y="0" fill="#C9DCEA" stroke="#181717" rx="5"/><text id="<td>" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="15.555" font-weight="normal"><tspan x="31.686" y="21.913"><td></tspan></text></g><g id="tr_1_td_1_text-node" transform="translate(0 518.507)"><ellipse id="Oval" cx="61.452" cy="30.246" fill="#1C85B5" stroke="#181717" rx="61.452" ry="30.246"/><text id="Shady-Grove" fill="#FFF" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="12.963" font-weight="normal"><tspan x="19.204" y="34.604">Shady Grove</tspan></text></g><g id="tr_1_td_2-node" transform="translate(147.87 458.015)"><rect id="Rectangle-path" width="96.019" height="34.567" x="0" y="0" fill="#C9DCEA" stroke="#181717" rx="5"/><text id="<td>" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="15.555" font-weight="normal"><tspan x="33.607" y="21.913"><td></tspan></text></g><g id="tr_1_td_2_text-node" transform="translate(134.427 518.507)"><ellipse id="Oval" cx="61.452" cy="30.246" fill="#1C85B5" stroke="#181717" rx="61.452" ry="30.246"/><text id="Aeolian" fill="#FFF" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="12.963" font-weight="normal"><tspan x="36.967" y="34.604">Aeolian</tspan></text></g><g id="tr_2_td_1-node" transform="translate(359.111 458.015)"><rect id="Rectangle-path" width="96.019" height="34.567" x="0" y="0" fill="#FFF" stroke="#181717" rx="5"/><text id="<td>" fill="#C06334" fill-rule="nonzero" font-family="PTMono-Regular, PT Mono" font-size="15.555" font-weight="normal"><tspan x="27.846" y="22.37"><td></tspan></text></g><g id="tr_2_td_1_text-node" transform="translate(345.669 518.507)"><ellipse id="Oval" cx="61.452" cy="30.246" fill="#1C85B5" stroke="#181717" rx="61.452" ry="30.246"/><g id="Group" fill="#FFF" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="12.963" font-weight="normal" transform="translate(12.482 14.555)"><text id="Over-the-River,"><tspan x=".48" y="14">Over the River,</tspan></text><text id="Charlie"><tspan x="25.925" y="31.284">Charlie</tspan></text></g></g><g id="tr_2_td_2-node" transform="translate(493.538 458.015)"><rect id="Rectangle-path" width="96.019" height="34.567" x="0" y="0" fill="#C9DCEA" stroke="#181717" rx="5"/><text id="<td>" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="15.555" font-weight="normal"><tspan x="33.607" y="21.913"><td></tspan></text></g><g id="tr_2_td_2_text-node" transform="translate(480.096 518.507)"><ellipse id="Oval" cx="61.452" cy="30.246" fill="#1C85B5" stroke="#181717" rx="61.452" ry="30.246"/><text id="Dorian" fill="#FFF" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="12.963" font-weight="normal"><tspan x="39.848" y="34.604">Dorian</tspan></text></g></g><g id="edges" stroke="#7E7C7B" stroke-width="2" transform="translate(61.452 34.567)"><path id="window-document" fill="#7E7C7B" d="M230.446 0v19.876"/><path id="document-html" fill="#7E7C7B" d="M230.446 60.493v19.876"/><path id="html-body" fill="#7E7C7B" d="M230.446 120.985v19.876"/><path id="body-table" fill="#7E7C7B" d="M230.446 181.478v19.876"/><path id="table-tbody" fill="#7E7C7B" d="M230.446 241.97v19.876"/><path id="tbody-tr_1" d="M230.446 302.463c0 11.522-16.003 17.283-48.01 17.283H86.417c-12.802 0-19.204 6.626-19.204 19.876"/><path id="tbody-tr_2" d="M230.446 302.463c0 11.522 22.404 17.283 67.213 17.283h96.02c12.802 0 19.203 6.626 19.203 19.876"/><path id="tr_1-tr_2_td_1" d="M67.213 380.239c0 11.522-6.4 17.283-19.203 17.283H19.204C6.4 397.522 0 404.148 0 417.4"/><path id="tr_1-tr_2_td_2" d="M67.213 380.239c0 11.522 6.402 17.283 19.204 17.283h28.806c12.802 0 19.204 6.626 19.204 19.877"/><path id="tr_2-tr_2_td_1" d="M412.882 380.239c0 11.522-6.401 17.283-19.204 17.283h-28.805c-12.803 0-19.204 6.626-19.204 19.877"/><path id="tr_2-tr_2_td_2" d="M412.882 380.239c0 11.522 12.803 17.283 38.408 17.283h9.602c12.802 0 19.204 6.626 19.204 19.877"/><path id="tr_1_td_1-text" fill="#7E7C7B" d="M0 458.015v19.876"/><path id="tr_1_td_2-text" fill="#7E7C7B" d="M134.427 458.015v19.876"/><path id="tr_2_td_1-text" fill="#7E7C7B" d="M345.669 458.015v19.876"/><path id="tr_2_td_2-text" fill="#7E7C7B" d="M482.016 458.015v19.876"/></g><g id="event-flow" transform="translate(103.7 15.443)"><g id="target_phase" transform="translate(186.757 442.572)"><text id="Target" fill="#C06334" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="17.284" font-weight="normal"><tspan x=".48" y="34.148">Target</tspan></text><text id="Phase" fill="#C06334" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="17.284" font-weight="normal"><tspan x="0" y="51.431">Phase</tspan></text><text id="(2)" fill="#C06334" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="17.284" font-weight="normal"><tspan x="15.363" y="68.715">(2)</tspan></text><rect id="Rectangle-path" width="96.019" height="34.567" x="68.654" y="0" stroke="#C06334" stroke-width="4" rx="5"/></g><g id="capture_phase"><text id="Capture" fill="#D35155" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="17.284" font-weight="normal"><tspan x="0" y="165.615">Capture</tspan></text><text id="Phase" fill="#D35155" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="17.284" font-weight="normal"><tspan x="7.201" y="182.899">Phase</tspan></text><text id="(1)" fill="#D35155" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="17.284" font-weight="normal"><tspan x="22.564" y="200.182">(1)</tspan></text><path id="capture_phase_arrow" stroke="#D35155" stroke-width="3" d="M116.183.112c-38.408-2.593-38.408 40.616 2.88 49.258"/><g id="capture_phase_arrow-link" stroke="#D35155" stroke-width="3" transform="translate(92.178 59.74)"><path id="capture_phase_arrow" d="M28.806.864c-38.408-2.592-38.408 40.617 2.88 49.258"/></g><g id="capture_phase_arrow-link" stroke="#D35155" stroke-width="3" transform="translate(96.98 120.233)"><path id="capture_phase_arrow" d="M28.806.864c-38.408-2.592-38.408 40.617 2.88 49.258"/></g><g id="capture_phase_arrow-link" stroke="#D35155" stroke-width="3" transform="translate(96.98 180.725)"><path id="capture_phase_arrow" d="M28.806.864c-38.408-2.592-38.408 40.617 2.88 49.258"/></g><g id="capture_phase_arrow-link" stroke="#D35155" stroke-width="3" transform="translate(96.98 241.218)"><path id="capture_phase_arrow" d="M28.806.864c-38.408-2.592-38.408 40.617 2.88 49.258"/></g><path id="capture_phase_arrow2" stroke="#D35155" stroke-width="3" d="M125.785 302.575c-38.408-2.593-38.408 82.097 175.715 69.134"/><path id="capture_phase_arrow3" stroke="#D35155" stroke-width="3" d="M301.5 384.672c-94.099-2.593-94.099 51.85-62.412 64.813"/></g><g id="bubble_phase" transform="translate(250.61 12.21)"><text id="Bubbling" fill="#478964" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="17.284" font-weight="normal"><tspan x="72.975" y="239.822">Bubbling</tspan></text><text id="Phase" fill="#478964" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="17.284" font-weight="normal"><tspan x="83.057" y="257.106">Phase</tspan></text><text id="(3)" fill="#478964" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="17.284" font-weight="normal"><tspan x="98.42" y="274.39">(3)</tspan></text><path id="bubble_phase_arrow3" stroke="#478964" stroke-width="3" d="M112.342 437.275c132.507-56.172 132.507-67.406 67.214-64.814"/><path id="bubble_phase_arrow2" stroke="#478964" stroke-width="3" d="M182.436 358.634c38.408-8.641 38.408-50.986-180.516-59.628"/><path id="bubble_phase_arrow" stroke="#478964" stroke-width="3" d="M0 287.772c38.408-2.593 38.408-37.16 0-45.802"/><g id="bubble_phase_arrow-link" stroke="#478964" stroke-width="3" transform="translate(0 181.478)"><path id="bubble_phase_arrow" d="M0 45.801C38.408 43.21 38.408 8.641 0 0"/></g><g id="bubble_phase_arrow-link" stroke="#478964" stroke-width="3" transform="translate(0 120.985)"><path id="bubble_phase_arrow" d="M0 45.801C38.408 43.21 38.408 8.641 0 0"/></g><path id="bubble_phase_arrow4" stroke="#478964" stroke-width="3" d="M0 106.294c48.01-2.593 48.01-37.16 9.602-45.801"/><path id="bubble_phase_arrow" stroke="#478964" stroke-width="3" d="M9.602 45.801C57.612 43.21 57.612 8.641 19.204 0"/></g></g></g></g></svg> \ No newline at end of file diff --git a/2-ui/2-events/02-bubbling-and-capturing/eventflow@2x.png b/2-ui/2-events/02-bubbling-and-capturing/eventflow@2x.png deleted file mode 100644 index d52c4f70c9..0000000000 Binary files a/2-ui/2-events/02-bubbling-and-capturing/eventflow@2x.png and /dev/null differ diff --git a/2-ui/2-events/03-event-delegation/2-sliding-tree/task.md b/2-ui/2-events/03-event-delegation/2-sliding-tree/task.md index a84fba5d38..1013069300 100644 --- a/2-ui/2-events/03-event-delegation/2-sliding-tree/task.md +++ b/2-ui/2-events/03-event-delegation/2-sliding-tree/task.md @@ -11,4 +11,4 @@ importance: 5 要件: - イベントハンドラは1つだけ(移譲を使ってください) -- ノードタイトルの外側(空の巣ペース)のクリックでは何もしないようにしてください。 +- ノードタイトルの外側(空のスペース)のクリックでは何もしないようにしてください。 diff --git a/2-ui/2-events/03-event-delegation/article.md b/2-ui/2-events/03-event-delegation/article.md index e4d8b79d27..cd7310bc32 100644 --- a/2-ui/2-events/03-event-delegation/article.md +++ b/2-ui/2-events/03-event-delegation/article.md @@ -81,7 +81,7 @@ function highlight(td) { 当然、その `<strong>` でクリックが起きた場合、それは `event.target` の値になります。 -![](bagua-bubble.png) +![](bagua-bubble.svg) `table.onclick` ハンドラでは、このような `event.target` を取り、クリックが `<td>` の中で行われたのかそうでないのかを知る必要があります。 @@ -102,7 +102,7 @@ table.onclick = function(event) { 説明: 1. メソッド `elem.closest(selector)` はセレクタに合致する最も近い祖先を返します。我々のケースではソース要素から上昇し `<td>` を探します。 2. もし `event.target` がどの `<td>` の内側にもない場合、その呼出は `null` を返し、何もする必要はありません。 -3. ネストしたテーブルでは、`event.target` は現在のテーブルの外側ににある `<td>` かもしれません。なので、実際に *テーブルの* `<td>` かどうかをチェックします。 +3. ネストしたテーブルでは、`event.target` は現在のテーブルの外側にある `<td>` かもしれません。なので、実際に *テーブルの* `<td>` かどうかをチェックします。 4. もしそうであれば、強調表示します。 ## 移譲サンプル: マークアップ内のアクション @@ -113,7 +113,7 @@ table.onclick = function(event) { 例えば、"Save" と "Load", "Search" などのボタンをもつメニューを作りたいとします。そしてメソッド `save`, `load`, `search`.... を持つオブジェクトがあります。 -最初の考えは、各ボタンに別々のハンドラを割り当てる事かもしれません。しかし、よりエレガントな方法があります。私たちはメニュー全体に対してハンドラを追加し、呼び出すメソッドを持っているに対して `date-action` 属性を追加します。: +最初の考えは、各ボタンに別々のハンドラを割り当てる事かもしれません。しかし、よりエレガントな方法があります。私たちはメニュー全体に対してハンドラを追加し、呼び出すメソッドがあるボタンに対して `date-action` 属性を追加します。: ```html <button *!*data-action="save"*/!*>Click to Save</button> @@ -161,7 +161,7 @@ table.onclick = function(event) { </script> ``` -`this.onClick` は `(*)` で `this` がバインドされていることに注意してください。それは重要です。なぜなら、そうしていなければ `this` はメニューオブジェクトではなく DOM 要素を参照し、`this[action]` は我々が必要とするものではいからです。 +`this.onClick` は `(*)` で `this` がバインドされていることに注意してください。それは重要です。なぜなら、そうしていなければ `this` はメニューオブジェクトではなく DOM 要素を参照し、`this[action]` は我々が必要とするものではないからです。 したがって、この移譲が我々に与えれくれたものはなんでしょう? @@ -260,13 +260,13 @@ One more counter: <input type="button" value="2" data-counter> ```compare + 初期化の簡素化とメモリの節約: 多くのハンドラを追加する必要はありません。 -+ コードを減らす code: 要素を追加または削除するときに、ハンドラを追加/削除する必要はありません。 ++ より少ないコード: 要素を追加または削除するときに、ハンドラを追加/削除する必要はありません。 + DOM の変更: `innerHTML` などで要素を一括して追加/削除することができます ``` -移譲はには、もちろん制限があります: +移譲には、もちろん制限があります: ```compare - まず、イベントがバブリングする必要があります。バブリングしないイベントもあります。また低レベルのハンドラは `event.stopPropagation()` を使うべきではありません。 -- 2つ目に、移譲は CPU負荷を上げる可能性があります。なぜなら、コンテナレベルのハンドラは、関心があるかどうかに関わらずコンテナの任意の場所のイベントに反応するたmです。しかし通常その負荷は無視できるので、考慮しません。 +- 2つ目に、移譲は CPU負荷を上げる可能性があります。なぜなら、コンテナレベルのハンドラは、関心があるかどうかに関わらずコンテナの任意の場所のイベントに反応するためです。しかし通常その負荷は無視できるので、考慮しません。 ``` diff --git a/2-ui/2-events/03-event-delegation/bagua-bubble.png b/2-ui/2-events/03-event-delegation/bagua-bubble.png deleted file mode 100644 index f0433aeb18..0000000000 Binary files a/2-ui/2-events/03-event-delegation/bagua-bubble.png and /dev/null differ diff --git a/2-ui/2-events/03-event-delegation/bagua-bubble.svg b/2-ui/2-events/03-event-delegation/bagua-bubble.svg new file mode 100644 index 0000000000..c4cd1ee1e2 --- /dev/null +++ b/2-ui/2-events/03-event-delegation/bagua-bubble.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="369" height="216" viewBox="0 0 369 216"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="bagua-bubble.svg"><path id="Rectangle-210" fill="#DBAF88" d="M202.488 140L217 186H103l14.512-46z"/><path id="Rectangle-209" stroke="#91C2A3" stroke-width="18" d="M216.634 81l16.09 51H86.276l16.09-51h114.267z"/><path id="Rectangle-208" stroke="#EFA39F" stroke-width="18" d="M82.986 29h153.028l22.71 72H60.276l22.71-72z"/><path id="Fill-46" fill="#166388" d="M164.5 141v13.816a4.5 4.5 0 11-9 0V141h9zm0-31v13h-9v-13h9zM160 20.53a4.471 4.471 0 013.362 1.3l17.64 17.64a4.5 4.5 0 01-6.364 6.364L164.5 35.698V92h-9V35.7l-10.138 10.136a4.5 4.5 0 11-6.363-6.364l17.639-17.64a4.477 4.477 0 013.363-1.3z"/><text id="<table>" fill="#D35155" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="9" y="30"><table></tspan></text><text id="<td>" fill="#478964" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="36" y="136"><td></tspan></text><text id="<strong>" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="35" y="181"><strong></tspan></text><text id="event.target" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="271.537" y="181">event.target</tspan></text><path id="Line-30" stroke="#C06334" stroke-dasharray="3,6" stroke-linecap="square" stroke-width="2" d="M229.5 177.5h30"/></g></g></svg> \ No newline at end of file diff --git a/2-ui/2-events/03-event-delegation/bagua-bubble@2x.png b/2-ui/2-events/03-event-delegation/bagua-bubble@2x.png deleted file mode 100644 index 8cc5ca913f..0000000000 Binary files a/2-ui/2-events/03-event-delegation/bagua-bubble@2x.png and /dev/null differ diff --git a/2-ui/2-events/04-default-browser-action/article.md b/2-ui/2-events/04-default-browser-action/article.md index 82c50c1f58..437b190ae3 100644 --- a/2-ui/2-events/04-default-browser-action/article.md +++ b/2-ui/2-events/04-default-browser-action/article.md @@ -108,7 +108,7 @@ menu.onclick = function(event) { バブリングの停止が必要に見える実践的な例を見てみましょう。しかし、実際にはバブリングを止めることなく上手く行うことができます。 -デフォルトでは、`contextmen` イベント時(マウスの右クリック)のブラウザは、標準オプション付きのコンテキストメニューを表示します。私たちはそれを防いで、独自のメニューを表示することができます: +デフォルトでは、`contextmenu` イベント時(マウスの右クリック)のブラウザは、標準オプション付きのコンテキストメニューを表示します。私たちはそれを防いで、独自のメニューを表示することができます: ```html autorun height=50 no-beautify run <button>Right-click for browser context menu</button> @@ -187,7 +187,7 @@ menu.onclick = function(event) { </script> ``` -これで、すべて正しく動作します。ネストされた要素を持っており、それらが独自のコンテキストメニューを持っている場合も動作します。ただ、各 `contextmen` ハンドラの中で `event.defaultPrevented` を確認してください。 +これで、すべて正しく動作します。ネストされた要素を持っており、それらが独自のコンテキストメニューを持っている場合も動作します。ただ、各 `contextmenu` ハンドラの中で `event.defaultPrevented` を確認してください。 ```smart header="event.stopPropagation() と event.preventDefault()" 明らかに分かるように、`event.stopPropagation()` と `event.preventDefault()` (`return false` としても知られている)は2つの異なるものです。それらはお互い関係ありません。 diff --git a/2-ui/2-events/05-dispatch-events/article.md b/2-ui/2-events/05-dispatch-events/article.md index b7d88dc478..b5e9dde8a3 100644 --- a/2-ui/2-events/05-dispatch-events/article.md +++ b/2-ui/2-events/05-dispatch-events/article.md @@ -156,7 +156,7 @@ alert(event.clientX); // undefined, 未知のプロパティは無視されま `detail` プロパティは任意のデータを持てます。技術的には、通常の `new Event` オブジェクトを作った後、そこに任意のプロパティを割り当てる事ができるため、それなしでも生きていくことはできます。しかし、`CustomEvent` は他のイベントプロパティとの衝突を避けるための特別な `detail` フィールドを提供します。 -イベントクラスは "どのような種類のイベント" かを示し、もしイベントがカスタムであれば、それが何であるかを明確にするために `CusstomEvent` を使うべきです。 +イベントクラスは "どのような種類のイベント" かを示し、もしイベントがカスタムであれば、それが何であるかを明確にするために `CustomEvent` を使うべきです。 ## event.preventDefault() @@ -211,13 +211,13 @@ alert(event.clientX); // undefined, 未知のプロパティは無視されま ## イベント中のイベントは同期的です -通常、イベントは非同期に処理されます。つまり: ブラウザが `onclick` を処理しており、そのプロセスの中で新しいイベントが起きた場合、`onclick` が終わるまでそれは待ちます。 +通常、イベントは待ち行列に入れられてから処理されます。つまり: ブラウザが `onclick` を処理しており、そのプロセスの中で新しいイベントが起きた場合(例えばマウスが移動したなど)、その処理は待ち行列に入れられ、対応する `mousemove` のハンドラは `onclick` の処理が終了したあとに呼ばれます。 -例外は、あるイベントが別のイベントから開始された場合です。 +注目すべき例外は、あるイベントが別のイベントから開始された場合(例えば `dispatchEvent` の使用)です。そのようなイベントは直ちに処理されます: 新しいイベントハンドラが呼ばれ、それから現在のイベント処理が再開します。 -次に、制御はネストされたイベントハンドラに飛び、その後戻ってきます。 +例えば、以下のコードでは、 `menu-open` イベントは `onclick` の途中でトリガされます。 -例えば、ここではネストされた `menu-open` イベントは `onclick` の間、同期的に処理されます。: +これは、 `onclick` のハンドラの終了を待たずに直ちに処理されます: ```html run <button id="menu">Menu (click me)</button> @@ -239,11 +239,15 @@ alert(event.clientX); // undefined, 未知のプロパティは無視されま </script> ``` -ネストされたイベント `menu-open` はバブルアップし `document` で処理されることに注意してください。ネストされたイベントの伝搬は、外部のコード(`onclick`) に戻る前に完全に終了します。 +出力の順番は次のとおりです: 1 → nested → 2 -それは `dispatchEvent` についてだけでなく、他のケースも同様です。イベントハンドラ中の JavaScrit は別のイベントにつながるメソッドを呼び出すことができます -- それらも同期的に処理されます。 +ネストされたイベント `menu-open` は `document` で捕捉されることに注意してください。ネストされたイベントの伝搬や処理は、外部のコード(`onclick`) に戻る前に完全に終了します。 -もしそれが気に入らなければ、`onclick` の末尾に `dispatchEvent` (または他のイベントトリガ呼び出し) を置くか、不便であれば `setTimeout(..., 0)` で囲みます。: +それは `dispatchEvent` についてだけでなく、他のケースも同様です。もしイベントハンドラが他のイベントを引き起こすメソッドを呼び出すと – これらも同期的に、ネストされた関数の要領で処理されます。 + +これが気に入らないとしましょう。 `menu-open` や他のネストされたイベントとは無関係に `onclick` を最初に完全に処理したいとします。 + +その場合、 `dispatchEvent` (あるいは他のイベントを引き起こす呼び出し)を `onclick` の最後に置くか、もっと良い方法として、それらをゼロ遅延の `setTimeout` でラップします: ```html run <button id="menu">Menu (click me)</button> @@ -265,6 +269,10 @@ alert(event.clientX); // undefined, 未知のプロパティは無視されま </script> ``` +これで現在のコードの実行後に、 `menu.onclick` を含め、 `dispatchEvent` を非同期に実行できます。つまりイベントハンドラが完全に切り離されました。 + +出力の順番は次の通りとなりました: 1 → 2 → nested + ## サマリ イベントを生成するためには、最初にイベントオブジェクトを作成する必要があります。 diff --git a/2-ui/3-event-details/1-mouse-events-basics/article.md b/2-ui/3-event-details/1-mouse-events-basics/article.md index 35ebb98ada..65ad33087b 100644 --- a/2-ui/3-event-details/1-mouse-events-basics/article.md +++ b/2-ui/3-event-details/1-mouse-events-basics/article.md @@ -1,87 +1,96 @@ -# マウスイベントの基本 -マウスイベントは "マウス操作" だけでなく、互換性のためにタッチデバイスでもエミュレートされます。 +# マウスイベント -このチャプターでは、マウスイベントとそれらのプロパティの詳細について説明していきます。 +この章では、マウスイベントとそのプロパティの詳細を説明します。 -[cut] +注意: マウスイベント は "マウスデバイス" だけではなく、スマートフォンやタブレットなど互換性のためにエミュレートされた他のデバイスから来ることもあります。 -## マウスイベントタイプ +## マウスイベントの種類 -マウスイベントは2つのカテゴリに分けることができます: "シンプル" と "複雑" です。 - -### シンプルなイベント - -最も使用されるシンプルなイベントは: +ここまでで、既にいくつかのイベントは見てきました: `mousedown/mouseup` -: 要素上でマウスボタンがクリックされた/離された。 +: 要素上でマウスボタンがクリックされた/離されました。 `mouseover/mouseout` -: マウスポイントが要素に来る/でていく。 +: マウスポイントが要素に来る/出ていきました。 `mousemove` -: 要素上のすべてのマウス移動で、このイベントが発生します。 - -...他のイベントタイプもあります、それらは後ほど説明します。 - -### 複雑なイベント [$Complex events] +: 要素上でのマウス移動毎にこのイベントが発生します。 `click` -: `mousedown` の後に発生し、マウスの左ボタンを使われた場合は同じ要素に `mouseup` を送ります。 - -`contextmenu` -: マウスの右ボタンが使わた場合、`mousedown` の後に発生します。 +: `mousedown` の後に発生し、マウスの左ボタンを使われた場合は同じ要素に `mouseup` が発生します。 `dblclick` -: 要素をダブルクリックした後に発生します。 +: 短時間の間に同じ要素で2回クリックされた後に発生します。最近はめったに使われません。 -複雑なイベントはシンプルなイベントから作られます。なので、論理的にはそれ無しでもやっていけます。しかしそれらはとても便利なので存在しています。 +`contextmenu` +: マウスの右ボタンが使われた場合に発生します。が、コンテキストメニューを開く方法は他にもあります。例 特別なキーボードのキーを使用する場合です。この場合でもイベントが発生するので、正確にはマウスイベントではありません。 + +...他にもいくつかイベントがあります。後ほど説明します。 ### イベント順 -アクションは複数のイベントをトリガする場合があります。 +上の一覧を見てわかるように、ユーザアクションは複数のイベントを発生させることがあります。 -例えば、ボタンが押されたとき、クリックは最初に `mousedown` をトリガし、次に クリックが離されたときに `mouseup` と `click` をトリガします。 +例えば、左ボタンのクリックは最初に `mousedown` を発生させ、ボタンが押された後、それが離されたときに `mouseup` と `click` が発生します。 -1つのアクションが複数イベントを開始する場合、それらの順序は固定です。つまり、ハンドラは `mousedown` -> `mouseup` -> `click` の順番で呼び出されます。イベントは同じシーケンスで処理されます: `onmouseup` は `onclick` の実行の前に完了します。 +1つのアクションが複数イベントを発生させる場合、順序は決まっています。つまり、ハンドラは `mousedown` -> `mouseup` -> `click` の順番で呼び出されます。 ```online 下のボタンをクリックし、イベントを見てみてください。ダブルクリックも試してみてください。 -下のテストスタンドでは、すべてのマウスイベントが記録され、それらの間で1秒以上の遅延がある場合、水平ルーラーで区切っています。 +下のテストスタンドでは、すべてのマウスイベントが記録され、イベント間で1秒以上の時間がある場合には水平ルーラーで区切っています。 -マウスボタンを検出する `which` プロパティも見ることができます。 +また、マウスのボタンを検出するための `button` プロパティが見えますが、これについては後述します。 <input onmousedown="return logMouse(event)" onmouseup="return logMouse(event)" onclick="return logMouse(event)" oncontextmenu="return logMouse(event)" ondblclick="return logMouse(event)" value="Click me with the right or the left mouse button" type="button"> <input onclick="logClear('test')" value="Clear" type="button"> <form id="testform" name="testform"> <textarea style="font-size:12px;height:150px;width:360px;"></textarea></form> ``` -## ボタンを取得する: which +## マウスボタン + +クリック関連のイベントには常に `button` プロパティがあり、正確なマウスボタンが取得できます。 -クリック関連のイベントは常にボタンが取得できる `which` プロパティを持っています。 +通常、`click` や `contextmenu` では使いません。なぜなら前者は左クリックのときだけ、後者は -- 右クリックのときにだけ起こるからです。 -これは `click` や `contextmenu` では使われません。なぜなら前者は左クリックのときだけ、後者は -- 右クリックのときにだけ起こるからです。 +一方、`mousedown` と `mouseup` ハンドラが `event.button` を必要とする場合があります。これらのイベントはどちらのボタンでも発生するためです。このとき、`button` で "右マウスダウン" と "左マウスダウン" が区別できます。 -しかし、もし `mousedown` と `mouseup` を追跡している場合、それが必要です。なぜならそれらのイベントは左右どちらのボタンでもトリガするためです。したがって、`which` は "右マウスダウン" と "左マウスダウン" を区別することができます。 +`event.button` がとり得る値は以下です: -3つのとり得る値があります: +| ボタン状態 | `event.button` | +|--------------|----------------| +| 左ボタン (主) | 0 | +| 中央ボタン (補助) | 1 | +| 右ボタン (副) | 2 | +| X1 ボタン (戻る) | 3 | +| X2 ボタン (進む) | 4 | -- `event.which == 1` -- 左ボタン -- `event.which == 2` - 中央ボタン -- `event.which == 3` - 右ボタン +ほとんどのマウスデバイスは、左右の2つのボタンのみなので、取り得る値は `0` か `2` です。タッチデバイスもタップ時に同様のイベントを生成します。 -中央のボタンは今のところいくらかエキゾチックで、ほとんど使われていません。 +また、`event.buttons` というプロパティもあり、これは現在押されているすべてのボタンを整数値として持っています。実際にはこのプロパティはほとんど使われませんが、[MDN](mdn:/api/MouseEvent/buttons) で詳細が確認できます。 + +```warn header="`event.which` は非推奨です" +古いコードでは `event.which` プロパティが使われていることがあります。これは、ボタンを取得する古い非標準の方法で、以下の値が取られます: + +- `event.which == 1` – 左ボタン +- `event.which == 2` – 中央ボタン +- `event.which == 3` – 右ボタン + +現時点では、`event.which` は推奨されていないので使用しないでください。 +``` ## 修飾子: shift, alt, ctrl と meta すべてのマウスイベントは押された修飾子のキーに関する情報も含みます。 -プロパティは: +イベントプロパティ: + +- `shiftKey`: `key:Shift` +- `altKey`: `key:Alt` (or Mac は `key:Opt`) +- `ctrlKey`: `key:Ctrl` +- `metaKey`: Mac は `key:Cmd` -- `shiftKey` -- `altKey` -- `ctrlKey` -- `metaKey` (Mac では `key:Cmd`) +イベント時に対応するキーが押されていた場合 `true` になります。 例えば、下のボタンは `key:Alt+Shift` + クリック でのみ動作します: @@ -99,23 +108,26 @@ </script> ``` -```warn header="注意: Macでは通常は `Ctrl` キーではなく `Cmd` キーです" -Windows と Linux では、`key:Alt`, `key:Shift` と `key:Ctrl` の修飾子があります。Mac ではもう一つあります: `key:Cmd`、これはプロパティ `metaKey` に相当します。 +```warn header="注意: Macでは通常は `Ctrl` ではなく `Cmd` です" +Windows と Linux には、`key:Alt`, `key:Shift` と `key:Ctrl` の修飾子があります。Mac ではもう一つあります: `key:Cmd`、これはプロパティ `metaKey` に相当します。 + +多くのアプリケーションでは、Windows/Linux が `key:Ctrl` を使う時、Mac では `key:Cmd` を使います。 -殆どの場合、Windows/Linux が `key:Ctrl` を使う時、Mac の人々は `key:Cmd` を使います。そのため、Windows ユーザが `key:Ctrl+Enter` or `key:Ctrl+A` を押すとき、Mac ユーザは `key:Cmd+Enter` or `key:Cmd+A` などを押し、ほとんどのアプリは `key:Ctrl` の代わりに `key:Cmd` を使います。 +つまり: Windows ユーザが `key:Ctrl+Enter` あるいは `key:Ctrl+A` を押すとき、Mac ユーザは `key:Cmd+Enter` あるいは `key:Cmd+A` となります。 -したがって、`key:Ctrl` + クリック のような組み合わせをサポートしたいとき、Mac では `key:Cmd` + クリック を使うのが理にかなっています。それがMacユーザにとってより快適なのです。 +したがって、`key:Ctrl` + クリック のような組み合わせをサポートしたいとき、Mac では `key:Cmd` + クリック とするのが理にかなっています。それがMacユーザにとってより快適です。 -たとえ Mac ユーザに `key:Ctrl` + クリックを強制したいとしても、 -- それは難しいです。問題は -- `key:Ctrl` で左クリックすると、Mac では *右クリック* と解釈され、Windows/Linux のように `click` ではなく `contextmenu` イベントが生成されるからです。 +たとえ Mac ユーザに `key:Ctrl` + クリックを強制したいとしても、それは難しいです。問題は `key:Ctrl` で左クリックすると、Mac では *右クリック* と解釈され、Windows/Linux のように `click` ではなく `contextmenu` イベントが生成されるからです。 なので、すべての OS の人々に快適に感じてもらうためには、`ctrlKey` と一緒に `metaKey` を使うべきです。 JS-codeの場合、`if (event.ctrlKey || event.metaKey)` というチェックを意味します。 - ``` ```warn header="モバイルデバイスもあります" -キーボードの組み合わせは便利です。訪問者がキーボードを持っていれば、それは機能します。 あなたのデバイスがそれを持っていない場合でも、同じことをする別の方法があります。 +キーボードの組み合わせは便利です。利用者がキーボードを使う場合は、それを使えるようにします。 + +ですが、モバイルデバイスのように、デバイスにキーボードがない場合は、修飾キーがなくても動作するようにする必要があります。 ``` ## 座標: clientX/Y, pageX/Y @@ -125,63 +137,37 @@ JS-codeの場合、`if (event.ctrlKey || event.metaKey)` というチェック 1. ウィンドウに相対: `clientX` と `clientY`. 2. ドキュメントに相対: `pageX` と `pageY`. -例えば、 500x500 サイズのウィンドウを持っていて、マウスが左上端にあるとき、`clientX` と `clientY` は `0` です。そして、マウスが中央にある場合、ドキュメント内のどの場所にあっても、`clientX` と `clientY` は `250` です。それらは `position:fixed` に似ています。 +<info:coordinates> の章で、これらの違いについては既に説明しました。 + +簡単に言うと、ドキュメント相対座標 `pageX/Y` はドキュメントの左上の端から数えた数値で、ページがスクールしても変わりません。一方、`clientX/Y` は現在のウィンドウの左上の端からになります。そのため、ページがスクロールされると値は変わります。 + +例えば、 500x500 サイズのウィンドウがあり、マウスが左上端にあるとき、`clientX` と `clientY` はページがスクロールされているかに関係なく `0` です。 + +そして、マウスが中央にある場合、ドキュメント内のどの場所にあっても、`clientX` と `clientY` は `250` です。それらは `position:fixed` に似ています。 ````online -入力フィールドにマウスを移動し `clientX/clientY` を見てみてください (それは `iframe` の中にあるので、座標は `iframe` への相対です)。: +入力フィールドにマウスを移動し `clientX/clientY` を見てみてください (例は `iframe` の中にあるので、座標は `iframe` への相対です): ```html autorun height=50 <input onmousemove="this.value=event.clientX+':'+event.clientY" value="Mouse over me"> ``` ```` -ドキュメント相対座標は、ドキュメントの左上端から数え得られます。ウィンドウではありません。 -座標 `pageX`, `pageY` はドキュメントレベルでの `position:absolute` に似ています。 - -チャプター <info:coordinates> で座標についてより知ることができます。 - -## マウスダウンで選択をしない - -マウスクリックは邪魔になる副作用があります。ダブルクリックするとテキストを選択します。 +## マウスダウンでの選択を防ぐ -もし独自のクリックイベントを処理したい場合、"余分な" 選択はいまいちに見えます。 +マウスのダブルクリックは、インターフェースによってはじゃまになる副作業があります。 -例えば、下のテキストのダブルクリックは、我々のハンドラに加えてそのテキストを選択します。: +例えば、下のテキストをダブルクリックすると、ハンドラに加えてそのテキストを選択します: ```html autorun height=50 <b ondblclick="alert('dblclick')">Double-click me</b> ``` -選択を止めるために、CSS を使う方法があります: [CSS UI Draft](https://www.w3.org/TR/css-ui-4/) にある `user-select` プロパティです。 +左のマウスボタンを押した後、離さずにマウスを動かすとそれも選択範囲になりますが、この動作はしばしば望まれないことがあります。 -ほとんどのブラウザはプレフィックス付きでサポートしています: +選択を防ぐ方法はいくつかあり、<info:selection-range> の章を読んでください。 -```html autorun height=50 -<style> - b { - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - } -</style> - -Before... -<b ondblclick="alert('Test')"> - Unselectable -</b> -...After -``` - -これで、"Unselectable" のダブルクリックをしても、選択されていません。動作しているように見えます。 - -...しかし、潜在的な問題があります! テキストは本当に選択不能になります。たとえユーザが "Before" から選択を開始し、"After" まで選択したとしても、その選択は "Unselectable" の部分はスキップされます。私たちは本当にそのテキストを選択不可にしたいですか? - -殆どの場合、そうではありません。ユーザは、コピーまたはその他の必要性のために、テキストを選択する正当な理由がある可能性があります。 もしユーザにそれをさせることを許さなければ、それは邪魔になるかもしれません。 そのためこの解決策はそれほど良いものではありません。 - -私たちが欲しいものは、ダブルクリックで選択を防ぐ、それだけです。 - -テキスト選択は `mousedown` イベントにおけるデフォルトブラウザアクションです。したがって、代わりの解決策は `mousedown` を処理し、それを防ぐことです。次のようになります: +この特定のケースで、最も理にかなった方法はブラウザの `mousedown` のアクションを無効にすることです。これで、これらの選択を防ぐことができます: ```html autorun height=50 Before... @@ -191,29 +177,12 @@ Before... ...After ``` -今や、太字の要素はダブルクリックでは選択されません。 - -一方、その中のテキストは依然として選択可能です。選択はテキスト自身ではなく、その前後から始める必要があります。通常それは問題ありません。 +これで太字部分の要素はダブルクリックで選択されません。また左ボタンを押しても選択は開始されません。 - -````smart header="選択のキャンセル" -選択を *防ぐ* 代わりに、イベントハンドラの中で、"事後に" キャンセルすることができます。 - -このようにします: - -```html autorun height=50 -Before... -<b ondblclick="*!*getSelection().removeAllRanges()*/!*"> - Double-click me -</b> -...After -``` - -太字の要素をダブルクリックすると、選択を表示され、その後すぐに消えます。良くは見えませんが。 -```` +補足: 内側のテキストは依然として選択可能です。ですが、選択はそのテキスト自身ではなく、その前後から始める必要があります。通常ユーザにとってこれは問題になりません。 ````smart header="コピーを防止する" -コピー・ペーストからコンテンツを保護するために選択を無効にしたい場合、別のイベントを使用することができます: `oncopy` +ページのコンテンツをコピーペーストから守るために選択を無効にしたい場合は、別のイベントが利用できます: `oncopy` ```html autorun height=80 no-beautify <div *!*oncopy="alert('Copying forbidden!');return false"*/!*> @@ -222,25 +191,22 @@ Before... If you know JS or HTML, then you can get everything from the page source though. </div> ``` -`<div>` でテキストの一部をコピーしようとすると動作しません。なぜならデフォルトアクション `oncopy` が防止されているためです。 +`<div>` のテキストの一部をコピーしようとしても動作しません。なぜならデフォルトアクション `oncopy` が防止されているためです。 -確かに、ユーザーがHTMLソースを開くことを止めることはできませんが、誰もがHTMLソースを開く方法を知っているわけではありません。 +確かに、ユーザーがページのHTMLソースを開き、そこからコンテツを取ることを止めることはできません。が、誰もがHTMLソースを開く方法を知っているわけではありません。 ```` ## サマリ -マウスイベントは次のプロパティを持っています: +マウスイベントには次のプロパティがあります: -- ボタン: `which`. +- ボタン: `button`. - 修飾子 (押された場合 `true`): `altKey`, `ctrlKey`, `shiftKey` と `metaKey` (Mac). - - `key:Ctrl` を処理したい場合、Mac ユーザを忘れてはいけません、彼らは `key:Cmd` を使うので、`if (e.metaKey || e.ctrlKey)` とチェックするのが良いです。 + - `key:Ctrl` を扱いたい場合に Mac ユーザを忘れてはいけません、Mac では `key:Cmd` を使うので、`if (e.metaKey || e.ctrlKey)` とチェックするのが良いです。 - ウィンドウ相対座標: `clientX/clientY`. - ドキュメント相対座標: `pageX/clientX`. -下記のタスクでは、選択をクリックの不要な副作用として扱うことも重要です。 +`mousedown` のブラウザのデフォルトアクションはテキストの選択ですが、インターフェースにとってそれが良くない場合には選択を止めるべきです。 -それにはいくつかの方法があります、例えば: -1. CSS プロパティ `user-select:none` (ブラウザプレフィックス付きで)はそれを完全に無効にします。 -2. `getSelection().removeAllRanges()` を使って選択を事後にキャンセルします。 -3. `mousedown` を処理し、デフォルトアクションを防ぎます(通常はこれがベストです)。 +次の章では、ポインタの動きに関するイベントと、ポインタの下の要素の変化を追跡する方法についてより詳しく見ていきます。 diff --git a/2-ui/3-event-details/1-mouse-events-basics/head.html b/2-ui/3-event-details/1-mouse-events-basics/head.html index 461f0e85bd..815428a2c4 100644 --- a/2-ui/3-event-details/1-mouse-events-basics/head.html +++ b/2-ui/3-event-details/1-mouse-events-basics/head.html @@ -1,47 +1,50 @@ <script> { - let timer = 0; + let timer = 0; - function showmesg(t, form) { + function showmesg(t, form) { - if (timer==0) timer = new Date() + if (timer == 0) { + timer = new Date(); + } - let tm = new Date() - if (tm-timer > 300) { - t = '------------------------------\n'+t - } + let tm = new Date(); - let area = document.forms[form+'form'].getElementsByTagName('textarea')[0] + if (tm - timer > 300) { + t = '------------------------------\n' + t; + } - area.value += t + '\n'; - area.scrollTop = area.scrollHeight + let area = document.forms[form + 'form'].getElementsByTagName('textarea')[0]; - timer = tm - } + area.value += t + '\n'; + area.scrollTop = area.scrollHeight; - function logMouse(e) { - let evt = e.type; - while (evt.length < 11) evt += ' '; - showmesg(evt+" which="+e.which, 'test') - return false; - } + timer = tm; + } - function keyval(n) { - if (n == null) return 'undefined'; - let s = '' + n; - if (n >= 32 && n < 127) s += ' ' + String.fromCharCode(n); - while (s.length < 6) s += ' '; - return s; - } + function logMouse(e) { + let evt = e.type; + while (evt.length < 11) evt += ' '; + showmesg(evt + " button=" + e.button, 'test') + return false; + } + function keyval(n) { + if (n == null) return 'undefined'; + let s = '' + n; + if (n >= 32 && n < 127) s += ' ' + String.fromCharCode(n); + while (s.length < 6) s += ' '; + return s; + } - function logClear(form) { - timer = 0; - document.forms[form+'form'].getElementsByTagName('textarea')[0].value =''; - lines = 0; - } - window.logClear = logClear; - window.logMouse = logMouse; + function logClear(form) { + timer = 0; + document.forms[form+'form'].getElementsByTagName('textarea')[0].value =''; + lines = 0; + } + + window.logClear = logClear; + window.logMouse = logMouse; } </script> diff --git a/2-ui/3-event-details/10-onload-ondomcontentloaded/article.md b/2-ui/3-event-details/10-onload-ondomcontentloaded/article.md deleted file mode 100644 index e7834b5f19..0000000000 --- a/2-ui/3-event-details/10-onload-ondomcontentloaded/article.md +++ /dev/null @@ -1,245 +0,0 @@ -# ページのライフサイクル: DOMContentLoaded, load, beforeunload, unload - -HTML ページのライフサイクルは、3つの重要なイベントを持っています: - -- `DOMContentLoaded` -- ブラウザが HTML を完全に読み込み、DOM ツリーは構築されましたが、写真 `<img>` のような外部リソースやスタイルシートはまだ読み込まれていない可能性があります。 -- `load` -- ブラウザがすべてのリソース(画像, スタイルなど)を読み込みました。 -- `beforeunload/unload` -- ユーザがページを離れようとしているとき。 - -それぞれのイベントが役に立つ場合があります: - -- `DOMContentLoaded` イベント -- DOMは準備できたので、ハンドラは DOM ノードを調べ、インタフェースを初期化することができます。 -- `load` イベント -- 追加のリソースがロードされ、画像のサイズなどが取得できます(HTML/CSSで指定されていない場合)。 -- `beforeunload/unload` イベント -- ユーザが離れようとしており、ユーザが行ったページ上での変更を保存するかを確認したり、本当に離れたいかを尋ねることができます。 - -これらのイベントの詳細について見ていきましょう。 - -[cut] - -## DOMContentLoaded - -`DOMContentLoaded` イベントは `document` オブジェクトで発生します。 - -キャッチするためには `addEventListener` を使わなければなりません: - -```js -document.addEventListener("DOMContentLoaded", ready); -``` - -例: - -```html run height=200 refresh -<script> - function ready() { - alert('DOM is ready'); - - // イメージはまだロードされていません(キャッシュされてない限り), なのでサイズは 0x0 です - alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`); - } - -*!* - document.addEventListener("DOMContentLoaded", ready); -*/!* -</script> - -<img id="img" src="https://en.js.cx/clipart/train.gif?speed=1&cache=0"> -``` - -例では、`DOMContentLoaded` ハンドラはドキュメントがロードされたときに実行され、ページ読み込みは待ちません。したがって、`alert` で表示されるサイズはゼロです。 - -一見すると、`DOMContentLoaded` イベントはとてもシンプルです。DOM ツリーが準備できた -- ここのイベントです。しかし、特徴はほとんどありません。 - -### DOMContentLoaded と scripts - -ブラウザが最初にHTMLをロードし、テキスト中の `<script>...</script>` に出くわすと、DOM の構築を続けることができません。すぐにスクリプトを実行する必要があります。そのため、このようなスクリプトがすべて実行された後にのみ `DOMContentLoaded` は発生する可能性があります。 - -外部スクリプト (`src` を持つもの) も、スクリプトがロードされ実行されている間はDOM の構築は一時停止します。したがって、`DOMContentLoaded` は外部スクリプトも待ちます。 - -唯一の例外は、`async` や `defter` 属性をもつ外部スクリプトです。これらは、ブラウザにそのスクリプトは待たずに処理を続けるよう言います。なので、ユーザはスクリプトのロードが終わる前にページを見ることができ、パフォーマンスに良いです。 - -```smart header="`async` と `defer` に関する言葉" -属性 `async` と `defer` は外部スクリプトに対してのみ動作します。`src` がない場合には無視されます。 - -どちらも、ブラウザにページでの処理をつづけるよう言い、"バックグラウンド" でスクリプトをロードします。その後、ロードできたらスクリプトを実行します。したがって、スクリプトは DOM の構築とページレンダリングをブロックしません。 - -そららの間に2つの違いがあります。 - -| | `async` | `defer` | -|---------|---------|---------| -| 順番 | `async` を持つスクリプトは *読み込んだもの順* で実行します。ドキュメント順は関係ありません -- 最初にロードされたものが最初に実行されます。 | `defer` を持つスクリプトは常に *ドキュメント順* で実行されます。 | -| `DOMContentLoaded` | `async` を持つスクリプトは、ドキュメントがまだ完全にダウンロードされていなくても実行されることがあります。これはスクリプトが小さい or キャッシュされており、ドキュメントが十分長い場合に発生します。 | `defer` を持つスクリプトは、ドキュメントがロードされ、パースされた後(必要に応じて待機する)に実行されます。`DOMContentLoaded` の直前です。| - -そのため、`async` は完全に独立したスクリプトに対して使われます。 - -``` - -### DOMContentLoaded と styles - -外部のスタイルシートは DOM には影響しないので、`DOMContentLoaded` はそれらを待ちません。 - -しかし、落とし穴があります: スタイルの後にスクリプトがある場合、そのスクリプトはスタイルシートが実行されるのを待たなければなりません。: - -```html -<link type="text/css" rel="stylesheet" href="style.css"> -<script> - // スクリプトはスタイルシートが読み込まれるまで実行されません - alert(getComputedStyle(document.body).marginTop); -</script> -``` - -この理由は、上の例のように、スクリプトが座標や他のスタイルに依存した要素のプロパティを取得したい場合があるためです。当然、それはスタイルがロードされるのを待たなければいけません。 - -`DOMContentLoaded` がスクリプトを待つので、その前のスタイルも同様に待つことになります。 - -### 組み込みのブラウザの自動入力 - -Firefox, Chrome や Opera の自動入力は `DOMContentLoaded` で起こります。 - -例えば、ページがログインとパスワードのフォームを持っていて、ブラウザがその値を覚えていた場合、 `DOMContentLoaded` でそれらを自動入力しようとする場合があります(ユーザが許可している場合)。 - -なので、読み込みに時間のかかるスクリプトによって `DOMContentLoaded` が延びると、自動入力もまた待ちます。恐らくあなたもサイトによっては見たことがあるでしょう(ブラウザの自動入力を利用している場合) -- ログイン/パスワードフィールドの自動入力がすぐにはされず、ページが完全にロードされるまで遅延があります。実際、それは `DOMContentLoaded` イベントまでの遅延です。 - -外部スクリプトに対して `async` や `defer` を使う小さなメリットの1つは -- `DOMContentLoaded` をブロックせず、ブラウザの自動入力に遅延がないことです。 - -## window.onload - -`window` オブジェクトの `load` イベントはスタイルや画像、その他リソースを含めページ全体が読み込まれたときにトリガされます。 - -下の例では、`window.onload` がすべての画像を待つため、画像サイズを正しく表示します。: - -```html run height=200 refresh -<script> - window.onload = function() { - alert('Page loaded'); - - // この時、画像はロードされています - alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`); - }; -</script> - -<img id="img" src="https://en.js.cx/clipart/train.gif?speed=1&cache=0"> -``` - -## window.onunload - -訪問者がページを離れるとき、`unload` イベントが `window` でトリガされます。そこでは、関連するポップアップウィンドウを閉じるなど、遅延なく何かをすることができます。しかし、別のページへの遷移をキャンセルすることはできません。 - -そのためには、別のイベント `onbeforeunload` を使う必要があります。 - -## window.onbeforeunload - -訪問者がページを離れ始めたり、ウィンドウを閉じようとした場合、`beforeunload` ハンドラで追加の確認を尋ねることができます。 - -質問の文字列を返す必要があります。ブラウザはそれを表示します。 - -例えば: - -```js -window.onbeforeunload = function() { - return "There are unsaved changes. Leave now?"; -}; -``` - -```online -下の `<iframe>` でハンドラを設定するためにボタンをクリックしてください。その後、そのハンドラの実行を見るためにリンクをクリックしてみてください。: - -[iframe src="window-onbeforeunload" border="1" height="80" link edit] -``` - -```warn header="テキストを無視し、代わりに独自のメッセージを表示するブラウザもあります" -Chrome や Firefox のような一部のブラウザは文字列を無視し、代わりに独自のメッセージを表示します。これは安全のためであり、潜在的な誤解を招くメッセージやハックしたメッセージからユーザを保護するためにそのようになっています。 -``` - -## readyState - -ドキュメントがロードされた後に `DOMContentLoaded` ハンドラを設定すると何が起こるでしょうか? - -当然、実行されません。 - -ドキュメントが準備できたかどうかが定かでない場合があります。例えば `async` 属性を持つ外部スクリプトを読み込み、それが非同期に実行するような場合です。ネットワークに依存して、ドキュメントが完成する前に実行されるかもしれないし、その後かもしれず、定かではありません。従って、我々はドキュメントの現在の状態を知ることができるべきです。 - -`document.readyState` プロパティは我々にその情報を提供します。3つの値を取り得ます。: - -- `"loading"` -- ドキュメントがロード中です。 -- `"interactive"` -- ドキュメントは完全に読み込まれました。 -- `"complete"` -- ドキュメントは完全に読み込まれ、すべてのリソース(画像のような)も読み込まれました。 - -なので、 `document.readyState` をチェックし、ハンドラを設定、もしくはすでに準備出来ている場合はすぐにコードを実行することができます。 - -このように: - -```js -function work() { /*...*/ } - -if (document.readyState == 'loading') { - document.addEventListener('DOMContentLoaded', work); -} else { - work(); -} -``` - -状態が変わったときにトリガされる `readystatechange` イベントがあります。なので、次のようにしてこれらの状態を出力することができます。: - -```js run -// 現在の状態 -console.log(document.readyState); - -// 状態の変化を出力 -document.addEventListener('readystatechange', () => console.log(document.readyState)); -``` - -`readystatechange` イベントはドキュメントの読み込み状態を追跡する代替のメカニズムで、それは昔に登場しました。最近ではほとんど使われていませんが、完全性のために説明しておきます。 - -他のイベントの中で `readystatechange` の場所はどこでしょう? - -タイミングを見るために、ここではイベントを記録する `<iframe>`, `<img>` とハンドラを持つドキュメントがあります。: - -```html -<script> - function log(text) { /* output the time and message */ } - log('initial readyState:' + document.readyState); - - document.addEventListener('readystatechange', () => log('readyState:' + document.readyState)); - document.addEventListener('DOMContentLoaded', () => log('DOMContentLoaded')); - - window.onload = () => log('window onload'); -</script> - -<iframe src="iframe.html" onload="log('iframe onload')"></iframe> - -<img src="http://en.js.cx/clipart/train.gif" id="img"> -<script> - img.onload = () => log('img onload'); -</script> -``` - -動作例は [サンドボックスの中](sandbox:readystate) です。 - -典型的な出力: -1. [1] initial readyState:loading -2. [2] readyState:interactive -3. [2] DOMContentLoaded -4. [3] iframe onload -5. [4] readyState:complete -6. [4] img onload -7. [4] window onload - -角括弧内の数値はそれが発生したおおよその時間を示します。実際の時間はもう少し大きいですが、同じ数字でラベル付けされたイベントは、ほぼ同時に発生します(+- 数ミリ秒)。 - -- `document.readyState` は `DOMContentLoaded` の直前に `interactive` になります。これら2つのイベントは実際には同じことを意味します。 -- `document.readyState` は、すべてのリソース(`iframe` や `img`)がロードされたときに `complete` になります。ここでは、`img.onload` (`img` は最後のリソース) と `window.onload` がほぼ同じ時間に発生していることが分かります。`complete` 状態にスイッチすることは、`window.onload` と同じことを意味します。違いは、`window.onload` は常に他のすべての `load` ハンドラの後で動作するということです。 - -## サマリ - -ページのライフサイクルイベント: - -- `DOMContentLoaded` イベントは DOM が準備できたときに `document` 上でトリガされます。この段階では要素に対して JavaScript を適用することができます。 - - `async` または `defer` もつ外部のものを除いたすべてのスクリプトが実行されます。 - - 画像や他のリソースはまだ読み込み中かもしれません。 -- `window` での `load` イベントはページとすべてのリソースがロードされたときにトリガされます。通常はこんなに長く待つ必要はないため、めったに使われません。 -- `window` での `beforeunload` イベントは、ユーザがページから離れたいときにトリガされます。もし文字列を返すと、ブラウザはユーザが本当に離れたいかどうかの質問を表示します。 -- `window` での `unload` イベントはユーザが最終的に離れるときにトリガされます。ハンドラの中では遅延やユーザへの質問を含まないシンプルなことのみが可能です。その制限があるので、ほとんど使われません。 -- `document.readyState` はドキュメントの現在の状態で、変更は `readystatechange` イベントで追跡することが可能です。: - - `loading` -- ドキュメントは読み込み中です。 - - `interactive` -- ドキュメントはパースされ、`DOMContentLoaded` とほぼ同じ時間に発生しますが、その前に発生します。 - - `complete` -- ドキュメントとリソースが読み込まれました。`window.onload` とほぼ同じ時間に発生しますが、その前に起きます。 diff --git a/2-ui/3-event-details/11-onload-onerror/article.md b/2-ui/3-event-details/11-onload-onerror/article.md deleted file mode 100644 index 18e301904c..0000000000 --- a/2-ui/3-event-details/11-onload-onerror/article.md +++ /dev/null @@ -1,89 +0,0 @@ -# リソース読み込み: onload と onerror - -ブラウザは外部リソース -- スクリプト, iframse, 画像 など -- の読み込みを追跡することができます。 - -そのためのイベントが2つあります: - -- `onload` -- ロードが成功した, -- `onerror` -- エラーが発生した. - -## スクリプトの読み込み - -外部スクリプトにある関数を呼び出す必要があるとしましょう。 - -次のようにして動的にロードすることができます。: - -```js -let script = document.createElement('script'); -script.src = "my.js"; - -document.head.append(script); -``` - -...しかし、どうやってそのスクリプトの中で宣言された関数を実行するのでしょう?私たちはそのスクリプトの読み込みまで待つ必要があり、その後に初めて呼び出すことができます。 - -### script.onload - -主なヘルパーは `load` イベントです。スクリプトがロードされ、実行された後にトリガされます。 - -```js run untrusted -let script = document.createElement('script'); - -// 任意のドメインから任意のスクリプトがロードできます -script.src = "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js" -document.head.append(script); - -*!* -script.onload = function() { - // スクリプトはヘルパー関数 "_" を作ります - alert(_); // 関数は利用可能です -}; -*/!* -``` - -そのため、`onload` では、スクリプト変数の利用や関数の実行などが可能です。 - -...そして、仮に読み込みが失敗したらどうなるでしょう?例えば、そのようなスクリプトがない(404 エラー)もしくはサーバがない、サーバがダウンしている場合です。 - -### script.onerror - -スクリプトの読み込み(実行ではない)中に発生したエラーは `error` イベントで追跡することが可能です。 - -例えば、存在しないスクリプトを要求してみましょう: - -```js run -let script = document.createElement('script'); -script.src = "https://example.com/404.js"; // こんなスクリプトはありません -document.head.append(script); - -*!* -script.onerror = function() { - alert("Error loading " + this.src); // Error loading https://example.com/404.js -}; -*/!* -``` - -ここではエラーの詳細を取得することはできないことに注意してください。エラーが 404, 500, または他の何かだったのかは分かりません。単に読み込みに失敗したということだけです。 - -## 他のリソース - -`load` と `error` イベントは他のリソースに対しても機能します。そこには微妙な違いがあります。 - -例えば: - -`<img>`, `<link>` (外部のスタイルシート) -: `load` と `error` 両方のイベントは期待通りに機能します。 - -`<iframe>` -: iframe の読み込みが完了した時の `load` イベントのみです。ロードが成功した場合とエラーが発生した場合の両方をトリガーします。 これは歴史的な理由によるものです。 - -## サマリ - -画像 `<img>`, 外部スタイル, スクリプトや他のリソースは、それらの読み込みを追跡するために `load` と `error` イベントを提供しています。: - -- `load` はロードが成功したときにトリガされます。 -- `error` はロードに失敗したときにトリガされます。 - -唯一の例外は `<iframe>` です: 歴史的な理由により、どんな完了にもかかわらず(たとえページが見つからなくても)、常に `load` をトリガします。 - -`readystatechange` イベントもリソースに対して機能しますが、殆ど使われません。なぜなら `load/error` イベントの方がシンプルなためです。 \ No newline at end of file diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md index 8af0821413..00035afe63 100644 --- a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md @@ -8,7 +8,7 @@ `mouseoever` イベントはマウスポインタが要素の上に来るときに発生し、`mouseout` は -- そこを離れるときです。 -![](mouseover-mouseout.png) +![](mouseover-mouseout.svg) これらのイベントは `relatedTarget` を持っているという点で特別です。 @@ -46,7 +46,7 @@ つまり、訪問者がマウスをとても速く動かしている場合、DOM 要素はスキップされる可能性があることを意味します。: -![](mouseover-mouseout-over-elems.png) +![](mouseover-mouseout-over-elems.svg) もしもマウスが上に書いているように、 `#FROM` から `#TO` 要素へ非常に速く移動する場合、間にある `<div>` (やそれら) はスキップされる可能性があります。`mouseout` イベントは `#FROM` でトリガし、その後 `#TO` ですぐに `mouseover` をトリガするかもしれません。 @@ -56,7 +56,7 @@ 特に、ウィンドウの外からページ中央にカーソルが移動することもあり得ます。そして、それは "どこからも" 来ていないので、`relatedTarget=null` です。: -![](mouseover-mouseout-from-outside.png) +![](mouseover-mouseout-from-outside.svg) <div style="display:none"> In case of a fast move, intermediate elements may trigger no events. But if the mouse enters the element (`mouseover`), when we're guaranteed to have `mouseout` when it leaves it. @@ -74,9 +74,9 @@ HTMLは2つのネストされた `<div>` 要素です。もしマウスをすば ## 子へ向けて移動するときの "余分な" mouseout -想像してください -- マウスポインタが要素に入りました。`mouserover` がトリガされました。その後、カーソルが子要素へ行きます。興味深いことは `mouseout` がその場合にトリガすることです。カーソルは依然として要素の中にありますが、`mouserout` が起きます! +想像してください -- マウスポインタが要素に入りました。`mouseover` がトリガされました。その後、カーソルが子要素へ行きます。興味深いことは `mouseout` がその場合にトリガすることです。カーソルは依然として要素の中にありますが、`mouseout` が起きます! -![](mouseover-to-child.png) +![](mouseover-to-child.svg) 奇妙に見えますが、簡単に説明する事ができます。 @@ -88,7 +88,7 @@ HTMLは2つのネストされた `<div>` 要素です。もしマウスをすば 赤の `<div>` は青の `<div>` にネストされています。青の `<div>` は以下のテキストにすべてのイベントを記録する `mouseover/out` ハンドラを持っています。 -青要素に入って、次に赤要素にマウスを移動させてみてください -- としてイベントを見てください。: +青要素に入って、次に赤要素にマウスを移動させてみてください -- そしてイベントを見てください。: [codetabs height=360 src="mouseoverout-child"] @@ -96,7 +96,7 @@ HTMLは2つのネストされた `<div>` 要素です。もしマウスをすば 2. 次に、青から赤要素へ移動した後、 -- `mouseout [target: blue]` を得ます(親を離れます)。 3. ...そしてすぐに `mouseover [target: red]` です。 -なので、`target` を考慮しないハンドラでは、`(2)` の `mouseout` で親を離れ、`(3)` の `mouserover` でそこへ戻ってきたように見えます。 +なので、`target` を考慮しないハンドラでは、`(2)` の `mouseout` で親を離れ、`(3)` の `mouseover` でそこへ戻ってきたように見えます。 要素の出入りの際にいくつかのアクションを実行する場合、多くの余分な "偽の" 実行が発生します。シンプルな物事に対して気づかない可能性があります。複雑な物事に対しては、望ましくない副作用を引き起こす可能性があります。 @@ -183,5 +183,5 @@ The details are in the [full example](sandbox:mouseenter-mouseleave-delegation-2 - 速いマウス移動は `mouseover, mousemove, mouseout` に対し、中間要素をスキップすることができます。 - イベント `mouseover/out` と `mouserenter/leave` は `relatedTarget` という追加のターゲットを持っています。それは私たちが 来た/行く 要素であり、`target` と相補的な要素です。 -- イベント `mouserover/out` は親要素から子要素に移動してもトリガされます。 マウスは、一度に1つの要素、つまり最も深い要素を想定します。 +- イベント `mouseover/out` は親要素から子要素に移動してもトリガされます。 マウスは、一度に1つの要素、つまり最も深い要素を想定します。 - イベント `mouserenter/leave` はバブルしないので、マウスが子要素に行くときにはトリガしません。それらは、マウスが要素全体の内側と外側のどちらに来るのかを追跡します。 diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside.png b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside.png deleted file mode 100644 index ea57233073..0000000000 Binary files a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside.png and /dev/null differ diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside.svg b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside.svg new file mode 100644 index 0000000000..22335b52e1 --- /dev/null +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="440" height="183" viewBox="0 0 440 183"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><path id="path-1" d="M202 57h126v93H202z"/><mask id="mask-2" width="126" height="93" x="0" y="0" fill="#fff" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox"><use xlink:href="#path-1"/></mask></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="mouseover-mouseout-from-outside.svg"><use id="Rectangle-10" fill="#FBF2EC" stroke="#DBAF88" stroke-dasharray="5,1,5,1" stroke-width="8" mask="url(#mask-2)" opacity=".6" xlink:href="#path-1"/><g id="noun_69008_cc" fill="#DBAF88" transform="translate(171 8)"><path id="Shape" d="M234.02 169H4.98C2.23 169 0 167.099 0 164.775V4.225C0 1.901 2.23 0 4.98 0h229.04c2.74 0 4.98 1.901 4.98 4.225v160.55c0 2.324-2.24 4.225-4.98 4.225zM9.959 160.55h219.084V8.45H9.958v152.1z"/><path id="Shape" d="M229.042 42.25H9.958c-2.748 0-4.979-1.901-4.979-4.225S7.21 33.8 9.96 33.8h219.083c2.738 0 4.979 1.901 4.979 4.225s-2.24 4.225-4.98 4.225zM27.917 20.9c0 2.21-1.79 4-4 4s-4-1.79-4-4 1.79-4 4-4 4 1.79 4 4zM47.833 20.9c0 2.21-1.79 4-4 4s-4-1.79-4-4 1.79-4 4-4 4 1.79 4 4zM67.75 20.9c0 2.21-1.79 4-4 4s-4-1.79-4-4 1.79-4 4-4 4 1.79 4 4z"/></g><path id="Rectangle-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M232 86h67v34h-67z"/><text id="#TO" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="250.857" y="109">#TO</tspan></text><path id="Fill-21" fill="#C06334" d="M223.128 94.635l-5.784 7.219H105.198l3.15-7.708-26.348 9.5 26.348 9.5-3.113-7.708h111.97l5.923 7.392H236l-7.29-9.097 7.29-9.098z" transform="matrix(-1 0 0 1 318 0)"/><text id="target" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="240" y="80">target</tspan></text><text id="relatedTarget-=-null" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="1" y="85">relatedTarget = null</tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside@2x.png b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside@2x.png deleted file mode 100644 index 6a17ca99f7..0000000000 Binary files a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-from-outside@2x.png and /dev/null differ diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems.png b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems.png deleted file mode 100644 index babbea5027..0000000000 Binary files a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems.png and /dev/null differ diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems.svg b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems.svg new file mode 100644 index 0000000000..437f03b102 --- /dev/null +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="508" height="92" viewBox="0 0 508 92"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><path id="path-1" d="M124 31h69.007v52.451H124z"/><path id="path-3" d="M207 31h69.007v52.451H207z"/><path id="path-5" d="M291 31h69.007v52.451H291z"/><mask id="mask-2" width="69.007" height="52.451" x="0" y="0" fill="#fff" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox"><use xlink:href="#path-1"/></mask><mask id="mask-4" width="69.007" height="52.451" x="0" y="0" fill="#fff" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox"><use xlink:href="#path-3"/></mask><mask id="mask-6" width="69.007" height="52.451" x="0" y="0" fill="#fff" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox"><use xlink:href="#path-5"/></mask></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="mouseover-mouseout-over-elems.svg"><path id="Rectangle-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M375 22h88.014v50.451H375z"/><text id="#TO" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="404.857" y="53">#TO</tspan></text><path id="Rectangle-7" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M17 22h88.014v50.451H17z"/><text id="#FROM" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="36.347" y="53">#FROM</tspan></text><use id="Rectangle-8" fill="#FBF2EC" stroke="#DBAF88" stroke-dasharray="5,1,5,1" stroke-width="8" mask="url(#mask-2)" opacity=".6" xlink:href="#path-1"/><text id="<DIV>" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold" opacity=".6"><tspan x="138.964" y="63"><DIV></tspan></text><use id="Rectangle-9" fill="#FBF2EC" stroke="#DBAF88" stroke-dasharray="5,1,5,1" stroke-width="8" mask="url(#mask-4)" opacity=".6" xlink:href="#path-3"/><text id="<DIV>-2" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold" opacity=".6"><tspan x="221.964" y="63"><DIV></tspan></text><use id="Rectangle-10" fill="#FBF2EC" stroke="#DBAF88" stroke-dasharray="5,1,5,1" stroke-width="8" mask="url(#mask-6)" opacity=".6" xlink:href="#path-5"/><text id="<DIV>-3" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold" opacity=".6"><tspan x="305.964" y="63"><DIV></tspan></text><path id="Fill-21" fill="#C06334" d="M401 38.635h-25.66l-11.53 7.219H140.245l6.28-7.708L94 47.646l52.526 9.5-6.207-7.708H363.53l11.81 7.392H401l-14.534-9.097L401 38.635" transform="matrix(-1 0 0 1 495 0)"/><text id="mouseover" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="340" y="17">mouseover</tspan></text><text id="mouseout" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="75" y="18">mouseout</tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems@2x.png b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems@2x.png deleted file mode 100644 index e61891a5af..0000000000 Binary files a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout-over-elems@2x.png and /dev/null differ diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.png b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.png deleted file mode 100644 index 628aa330cf..0000000000 Binary files a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.png and /dev/null differ diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.svg b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.svg new file mode 100644 index 0000000000..1277ddff55 --- /dev/null +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="278" height="92" viewBox="0 0 278 92"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="mouseover-mouseout.svg"><path id="Rectangle-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M95 25h88.014v50.451H95z"/><text id="<DIV>" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="118.964" y="56"><DIV></tspan></text><path id="Fill-55" fill="#C06334" d="M109 51.828S88.159 39.088 74.327 34c-.336 2.104-.67 4.208-1.007 6.311-22.67-1.178-45.696.566-67.32 5.234l8.31 12.565c18.27-3.944 37.724-5.417 56.88-4.422-.337 2.105-.672 4.208-1.007 6.312C81.31 56.21 109 51.828 109 51.828"/><text id="mouseover" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="17" y="21">mouseover</tspan></text><path id="Fill-56" fill="#C06334" d="M268 51.828S247.159 39.088 233.327 34c-.336 2.104-.67 4.208-1.007 6.311-22.67-1.178-45.696.566-67.32 5.234l8.31 12.565c18.27-3.944 37.724-5.417 56.88-4.422-.337 2.105-.672 4.208-1.007 6.312C240.31 56.21 268 51.828 268 51.828"/><text id="mouseout" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="193" y="21">mouseout</tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout@2x.png b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout@2x.png deleted file mode 100644 index c320e2ccda..0000000000 Binary files a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-mouseout@2x.png and /dev/null differ diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child.png b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child.png deleted file mode 100644 index 627f9584a7..0000000000 Binary files a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child.png and /dev/null differ diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child.svg b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child.svg new file mode 100644 index 0000000000..78210845b4 --- /dev/null +++ b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="274" height="143" viewBox="0 0 274 143"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><filter id="filter-2" width="212.1%" height="203.6%" x="-52.6%" y="-43.3%" filterUnits="objectBoundingBox"><feMorphology in="SourceAlpha" operator="dilate" radius="1" result="shadowSpreadOuter1"/><feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1"/><feGaussianBlur in="shadowOffsetOuter1" result="shadowBlurOuter1" stdDeviation="1.5"/><feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"/><feColorMatrix in="shadowBlurOuter1" values="0 0 0 0 0.0941176471 0 0 0 0 0.0901960784 0 0 0 0 0.0901960784 0 0 0 1 0"/></filter><path id="path-1" d="M12.487 6.865L0 0l3.64 13.776 2.726-4.108 3.264 4.177 1.474-1.152-3.263-4.177z"/></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="mouseover-to-child.svg"><path id="Rectangle-210" fill="#DBAF88" d="M200.47 59L216 121H94l15.53-62z"/><path id="Rectangle-209" stroke="#91C2A3" stroke-width="18" d="M223.628 32l20.205 66H66.167l20.205-66h137.256z"/><path id="Fill-54" fill="#C06334" d="M125.435 52l-.683 3.884c-15.408-.725-31.056.348-45.752 3.22l5.647 7.733c12.417-2.427 25.638-3.334 38.656-2.721L122.62 68c7.56-2.333 26.38-5.03 26.38-5.03S134.836 55.132 125.435 52z" transform="rotate(33 114 60)"/><g id="Rectangle-237" transform="translate(145 82)"><use fill="#000" filter="url(#filter-2)" xlink:href="#path-1"/><path fill="#FFF" stroke="#181717" d="M-.781-1l14.484 7.963-5.041 1.792 3.144 4.025-2.262 1.767-3.145-4.025-2.958 4.459L-.78-1z"/></g><text id="mouseout" fill="#643B0C" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="60" y="37">mouseout</tspan></text><text id="mouseover" fill="#643B0C" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="145" y="76">mouseover</tspan></text><text id="#parent" fill="#91C2A3" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="14" y="21">#parent</tspan></text><text id="#child" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="14" y="126">#child</tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child@2x.png b/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child@2x.png deleted file mode 100644 index 51a917d86e..0000000000 Binary files a/2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseover-to-child@2x.png and /dev/null differ diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/field.svg b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/field.svg index 81ecde0286..4ae90b1c71 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/field.svg +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/field.svg @@ -1,32 +1 @@ -<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" - "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg width="22cm" height="14cm" viewBox="0 0 1150 720" stroke="white" version="1.1" - xmlns="http://www.w3.org/2000/svg"> - <!--图纸区--> - <rect x="0" y="0" width="1150" height="720" fill="green" /> - <!--球场框和中线--> - <path d="M 575,20 L 50,20 50,700 1100,700 1100,20 575,20 575,700 z" stroke="white" stroke-width="2" fill="green" /> - <!--中线--> - <circle cx="575" cy="360" r="91.5" stroke="white" stroke-width="2" fill-opacity="0" /> - <circle cx="575" cy="360" r="2" stroke="white" fill="white" /> - <!--罚球点--> - <circle cx="160" cy="360" r="2" stroke="white" fill="white" /> - <circle cx="990" cy="360" r="2" stroke="white" fill="white" /> - <!--球门--> - <path d="M 50,324.4 L 40,324.4 40, 396.6 50 396.6 z" stroke="white" stroke-width="2" fill-opacity="0" /> - <path d="M 1100,324.4 L 1110,324.4 1110,396.6 1100,396.6 z" stroke="white" stroke-width="2" fill-opacity="0" /> - <!--球门区--> - <path d="M 50,269.4 L 105,269.4 105,451.6 50 451.6 z" stroke="white" stroke-width="2" fill-opacity="0" /> - <path d="M 1100,269.4 L 1045,269.4 1045,451.6 1100,451.6 z" stroke="white" stroke-width="2" fill-opacity="0" /> - <!--禁区 --> - <path d="M 50,159.4 L 215,159.4 215,561.6 50 561.6 z" stroke="white" stroke-width="2" fill-opacity="0" /> - <path d="M 1100,159.4 L 935,159.4 935,561.6 1100,561.6 z" stroke="white" stroke-width="2" fill-opacity="0" /> - <path d="M 215,286.875 A 91.5,91.5 0 0,1 215,433.125 z" stroke="white" stroke-width="2" fill="green" /> - <path d="M 935,286.875 A 91.5,91.5 0 0,0 935,433.125 z" stroke="white" stroke-width="2" fill="green" /> - <!--角球弧--> - <path d="M 50,30 A 10,10 0 0,0 60,20 L 50,20 z" stroke="white" stroke-width="2" fill-opacity="0" /> - <path d="M 60,700 A 10,10 0 0,0 50,690 L 50,700 z" stroke="white" stroke-width="2" fill-opacity="0" /> - <path d="M 1100,690 A 10,10 0 0,0 1090,700 L 1100,700 z" stroke="white" stroke-width="2" fill-opacity="0" /> - <path d="M 1090,20 A 10,10 0 0,0 1100,30 L 1100,20 z" stroke="white" stroke-width="2" fill-opacity="0" /> -</svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="233" height="156" viewBox="0 0 233 156"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="field.svg"><image id="Screen-Shot-2017-02-25-at-23.45.22" width="224" height="150" x="4" y="3" opacity=".7" xlink:href="data:image/png;base64,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"/><text id="(0,0)" fill="#A7333A" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="45" y="60">(0,0)</tspan></text><circle id="Oval" cx="15.5" cy="15.5" r="4.5" fill="#C06334"/><text id="clientWidth" fill="#A7333A" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="73" y="114">clientWidth</tspan></text><path id="Line-9" fill="#C06334" fill-rule="nonzero" d="M24.114 22.183l20.711 4.719-5.078 6.181 13.705 11.258 1.16.952-1.905 2.318-1.16-.952-13.704-11.258-5.078 6.183-8.651-19.401z"/><path id="Line-10" fill="#C06334" fill-rule="nonzero" d="M197 118l19 9.5-19 9.5v-8H34v8l-19-9.5 19-9.5v8h163v-8z"/></g></g></svg> \ No newline at end of file diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/article.md b/2-ui/3-event-details/4-mouse-drag-and-drop/article.md index aa3e3a46d9..886028db19 100644 --- a/2-ui/3-event-details/4-mouse-drag-and-drop/article.md +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/article.md @@ -107,7 +107,7 @@ ball.style.top = pageY - ball.offsetHeight / 2 + 'px'; 例えば、ボールの端でドラッグを開始する場合、ドラッグ中のカーソルは端のままであるべきです。 -![](ball_shift.png) +![](ball_shift.svg) 1. 訪問者がボタン (`mousedown`) を押したとき -- 変数 `shiftX/shiftY` に、カーソルからボールの左上端の距離を覚えることができます。私たちはドラッグの間その距離を維持する必要があります。 @@ -277,22 +277,22 @@ function onMouseMove(event) { 今、プロセス全体で、変数 `currentDroppable` の中に現在の "ドロップターゲット" があり、強調表示やその他のことをするのに使うことができます。 -## Summary +## サマリ -We considered a basic `Drag'n'Drop` algorithm. +私達は、基本的なドラッグアンドドロップのアルゴリズムについて考察しました。 -The key components: +重要な要素: -1. Events flow: `ball.mousedown` -> `document.mousemove` -> `ball.mouseup` (cancel native `ondragstart`). -2. At the drag start -- remember the initial shift of the pointer relative to the element: `shiftX/shiftY` and keep it during the dragging. -3. Detect droppable elements under the pointer using `document.elementFromPoint`. +1. イベントの流れ: `ball.mousedown` -> `document.mousemove` -> `ball.mouseup`(ネイティブの `onstart` はキャンセル) +2. ドラッグの開始時 -- 要素に対するポインタの相対的なずれを記憶すること: `shiftX/shiftY` そしてドラッグ中保持しておくこと +3. ポインタの下にあるドロップ可能な要素を `document.elementFromPoint` を用いて検出すること -We can lay a lot on this foundation. +私達は、この機能の上に、様々な物を置くことができます。 -- On `mouseup` we can finalize the drop: change data, move elements around. -- We can highlight the elements we're flying over. -- We can limit dragging by a certain area or direction. -- We can use event delegation for `mousedown/up`. A large-area event handler that checks `event.target` can manage Drag'n'Drop for hundreds of elements. -- And so on. +- `mouseup` の段階で、ドロップを終了させることができます: データの変更、要素の移動 +- 今動かしている要素をハイライト表示できます。 +- ドラッグを特定の場所や方向に制限することができます +- `mousedown/up` において、イベントの委譲を行うことができます。 `event.target` を確認する広範囲のイベントハンドラが、何百もの要素のドラッグアンドドロップを管理することができます。 +- などなど。 -There are frameworks that build architecture over it: `DragZone`, `Droppable`, `Draggable` and other classes. Most of them do the similar stuff to described above, so it should be easy to understand them now. Or roll our own, because you already know how to handle the process, and it may be more flexible than to adapt something else. +これらを基にアーキテクチャを構築するフレームワークが存在します: `DragZone` 、 `Droppable` 、 `Draggable` や他のクラスなど。これらのほとんどは上記で説明したものと類似した事を行うため、今となっては簡単に理解できることでしょう。あるいは自作します。なぜなら既に、どのように処理を行えばよいかを知っているし、こちらの方が、何か他のものを採用するよりも柔軟性が高いでしょうから。 diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/ball_shift.png b/2-ui/3-event-details/4-mouse-drag-and-drop/ball_shift.png deleted file mode 100644 index 63fe372662..0000000000 Binary files a/2-ui/3-event-details/4-mouse-drag-and-drop/ball_shift.png and /dev/null differ diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/ball_shift.svg b/2-ui/3-event-details/4-mouse-drag-and-drop/ball_shift.svg new file mode 100644 index 0000000000..29fdb31ef0 --- /dev/null +++ b/2-ui/3-event-details/4-mouse-drag-and-drop/ball_shift.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="149" height="133" viewBox="0 0 149 133"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><path id="path-1" d="M93 75v21.429l5.25-5.358 4.375 8.929h1.75s1.13-1.161.875-1.786c-1.203-2.947-4.375-8.928-4.375-8.928H107L93 75z"/><path id="path-3" d="M13 5h111v111H13z"/><filter id="filter-2" width="195.6%" height="153.8%" x="-42.9%" y="-25.8%" filterUnits="objectBoundingBox"><feMorphology in="SourceAlpha" operator="dilate" radius="1" result="shadowSpreadOuter1"/><feOffset dy="1" in="shadowSpreadOuter1" result="shadowOffsetOuter1"/><feGaussianBlur in="shadowOffsetOuter1" result="shadowBlurOuter1" stdDeviation="1.5"/><feComposite in="shadowBlurOuter1" in2="SourceAlpha" operator="out" result="shadowBlurOuter1"/><feColorMatrix in="shadowBlurOuter1" values="0 0 0 0 0.0941176471 0 0 0 0 0.0901960784 0 0 0 0 0.0901960784 0 0 0 1 0"/></filter><mask id="mask-4" width="111" height="111" x="0" y="0" fill="#fff" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox"><use xlink:href="#path-3"/></mask></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="ball_shift.svg"><g id="ball" opacity=".354" transform="translate(14 6)"><circle id="Oval-4" cx="53" cy="53" r="53" fill="#FFF"/><path id="Shape" fill="#5E5C5C" fill-rule="nonzero" d="M108.95 52.524a.66.66 0 00-.009-.356l-.01-.044c-.711-14.312-6.856-27.567-17.308-37.338-1.28-1.33-3.19-2.902-5.37-4.426C72.119.137 53.779-2.73 37.187 2.697c-1.188.307-2.824.93-4.728 1.795a53.587 53.587 0 00-8.741 4.803 32.155 32.155 0 00-1.437 1.009 54.376 54.376 0 00-8.786 8C-6.322 40.792-4.047 75.325 18.562 95.281a54.894 54.894 0 0014.058 9.025c.733.35 1.342.56 1.86.784 21.382 8.552 45.892 2.77 61.028-14.403 9.271-10.528 14.042-24.071 13.442-38.164zm-1.951-2.264c-.048-.13-.095-.262-.147-.391-1.112-3-3.155-8.53-8.149-12.96-.737-9.313-4.052-14.758-7.006-19.544A52.812 52.812 0 01107 50.26zM70.587 9.112c.044-.01.09-.025.128-.037 4.802.84 12.238 3.568 18.634 7.928a.79.79 0 00.085.195l.62 1.001c2.851 4.62 6.08 9.85 6.808 18.739-2.901 2.139-4.968 4.418-6.974 6.623-1.195 1.314-2.425 2.667-3.826 3.99-4.698-4.376-13.03-8.118-22.027-9.868-.882-3.896-2.649-11.121-6.985-19.124.113-.13.222-.257.335-.383 6.969-7.914 9.423-8.364 12.266-8.883.301-.056.614-.114.936-.181zM45.423 53.884l18.033-14.431c9.045 1.723 17.37 5.492 21.758 9.848 1.073 3.326 1.349 10.492 1.557 15.765.1 2.7.197 5.06.364 6.482-7.083 6.463-16.481 13.096-19.546 13.786l-19.727-7.726c-1.476-8.27-1.908-13.934-2.439-23.724zm.59 23.87L31.731 87.81a.716.716 0 00-.427-.035c-2.664.553-12.913-6.968-17.907-11.335a.762.762 0 00-.183-.115c-.212-1.43-.666-3.247-1.178-5.31-1.352-5.44-3.026-12.18-1.523-16.37.03-.024.055-.05.083-.08.017-.02.036-.043.051-.068.976-1.405 1.918-2.621 2.884-3.712 3.048-3.463 6.502-5.923 11.874-8.444.353.257.717.519 1.094.795 5.001 3.654 11.805 8.628 17.089 10.963.533 9.738.964 15.407 2.425 23.657zM26.956 10.614c.202-.228.42-.426.673-.653l.14-.126c.534-.294 1.226-.7 2.025-1.168 1.883-1.102 4.46-2.611 7.286-4.035 7.63-2.608 15.88-3.433 23.912-2.4 0 .268.15.521.394.644.492.255.989.494 1.485.735C64.984 4.63 67 5.606 68.846 7.564c-2.886.547-5.838 1.43-12.868 9.41-.148.17-.298.34-.45.51-7.083-.704-15.227-1.093-22.551 1.071-2.709-3.23-4.985-4.767-7.001-6.127l-.054-.038c.44-1.03.766-1.474 1.034-1.777zM4.076 44.522a.721.721 0 00-1.147-.358 3.964 3.964 0 00-.218.193 52.024 52.024 0 0111.76-24.426c.35-.287.79-.672 1.294-1.111 2.085-1.817 6.947-6.062 8.49-5.327a39.018 39.018 0 00.267.18l.425.287c1.88 1.273 4.006 2.707 6.51 5.655-2.252 4.784-6.967 16.6-6.944 21.106-5.554 2.618-9.16 5.198-12.36 8.833a40.526 40.526 0 00-2.532 3.198c-.1-.092-.203-.192-.307-.287-1.605-1.5-4.025-3.768-5.238-7.943zm15.678 49.407A52.861 52.861 0 018.367 79.731c1.991-.56 2.949-.931 4.067-1.69 1.838 1.556 13.222 11.043 18.26 11.56a105.868 105.868 0 008.806 12.392c-1.647 1.015-3.003 1.365-4.75 1.261a52.782 52.782 0 01-14.996-9.325zm18.65 10.67c.849-.312 1.71-.774 2.654-1.413 7.942 1.173 16.8.439 23.033-.438.09.06.19.096.303.112 1.495.198 5.943.655 11.3.044-11.761 5.084-24.994 5.706-37.29 1.695zm54.134-13.757a.74.74 0 00-.12.523c-3.287 3.39-7 6.302-11.042 8.666a.679.679 0 00-.32.008c-6.468 1.702-12.663 1.383-15.434 1.102 1.247-3.108 2.104-7.732 2.6-14.068 3.637-1.017 12.846-7.486 20.207-14.21 1.834.303 3.085.589 4.1.825 2.47.571 3.603.835 8.098.248-.58 3.376-2.206 8.355-8.089 16.906zm8.064-10.453c1.264-3.02 1.745-5.273 1.99-7.064a.686.686 0 00-.042-.36c1.125-2.556 3.419-9.788 4.597-15.706-.361 8.187-2.605 16.075-6.545 23.13z"/></g><text id="shiftX" fill="#C06334" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="27.8" y="87">shiftX</tspan></text><text id="shiftY" fill="#C06334" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="94.8" y="34">shiftY</tspan></text><g id="Default"><use fill="#000" filter="url(#filter-2)" xlink:href="#path-1"/><path fill="#FFF" stroke="#A7333A" d="M92.5 73.775l15.69 16.01h-6.488c.886 1.695 3.06 5.91 4.01 8.24.318.776-.979 2.324-.979 2.324h0l-2.42.151-4.2-8.574-5.613 5.727V73.775z"/></g><use id="Rectangle" stroke="#7E7C7B" stroke-dasharray="1,1" stroke-width="2" mask="url(#mask-4)" xlink:href="#path-3"/><path id="Line-41-Copy-2" fill="#C06334" fill-rule="nonzero" d="M78.295 66l14 7-14 7-.001-6H28v6l-14-7 14-7v6h50.294l.001-6z"/><path id="Line-41-Copy-3" fill="#C06334" fill-rule="nonzero" d="M92.295 5l7 14-6.001-.001v40l6.001.001-7 14-7-14h5.999V19h-5.999l7-14z"/></g></g></svg> \ No newline at end of file diff --git a/2-ui/3-event-details/4-mouse-drag-and-drop/ball_shift@2x.png b/2-ui/3-event-details/4-mouse-drag-and-drop/ball_shift@2x.png deleted file mode 100644 index d035705e6a..0000000000 Binary files a/2-ui/3-event-details/4-mouse-drag-and-drop/ball_shift@2x.png and /dev/null differ diff --git a/2-ui/3-event-details/5-keyboard-events/article.md b/2-ui/3-event-details/5-keyboard-events/article.md deleted file mode 100644 index 3cd36881b1..0000000000 --- a/2-ui/3-event-details/5-keyboard-events/article.md +++ /dev/null @@ -1,171 +0,0 @@ -# キーボード: keydown と keyup - -キーボードを学ぶ前に、現代のデバイスでは "何かを入力する" ために他の方法があることに留意してください。例えば、人々は音声認識(特にモバイルデバイスで)や、マウスによるコピーペーストを使います。 - -したがって、`<input>` フィールドへの任意の入力を追跡したい場合、キーボードイベントだけでは十分ではありません。`<input>` フィールドのどのような方法での変更も処理するために `input` と言う名前の別のイベントがあります。そしてこのようなタスクに対する良い選択である場合があります。それらに関しては、チャプター <info:events-change-input> で後ほど説明します。 - -キーボードイベントは、キーボードアクション(仮想キーボードも含む)を処理したいときに使われるべきです。例えば、矢印キー `key:Up` や `key:Down`、またはホットキー (キーの組み合わせを含む)に反応するために使います。 - -[cut] - - -## テストスタンド - -```offline -キーボードイベントをより良く理解するために、[テストスタンド](sandbox:keyboard-dump)が使えます。 -``` - -```online -キーボードイベントをよりよく理解するために、下のテストスタンドがあります。 - -テキストフィールドの中で、様々なキーの組み合わせを試してみてください。 - -[codetabs src="keyboard-dump" height=480] -``` - - -## Keydown と keyup - -`keydown` イベントはキーが押された時に、そして `keyup` は -- それが離されたときに発生します。 - -### event.code と event.key - -イベントオブジェクトの `key` プロパティは、イベントオブジェクトの `code` プロパティが "物理的なキーコード" を取得できる一方、文字を取得することができます。 - -例えば、同じキー `key:Z` が `Shift` あり / なし で押される場合があります。それは2つの異なる文字を返します: 小文字の `z` と大文字の `Z` です。 - -`event.key` はまさに文字であり、それは異なるでしょう。しかし、`event.code` は同じです: - - -| キー | `event.key` | `event.code` | -|--------------|-------------|--------------| -| `key:Z` |`z` (小文字) |`KeyZ` | -| `key:Shift+Z`|`Z` (大文字) |`KeyZ` | - - -ユーザが異なる言語で作業する場合、別の言語に切り替えると、`"Z"` の代わりに全く違う文字になります。これは `event.key` の文字になりますが、その一方で `event.code` は常に同じ `"KeyZ"` です。 - -```smart header="\"KeyZ\" とその他のキーコード" -すべてのキーは、キーボード上の位置に応じたコードを持っています。キーコードは[UI イベントコード仕様](https://www.w3.org/TR/uievents-code/)で記載されています。 - -例えば: -- 文字キーはコード `"Key<letter>"` です: `"KeyA"`, `"KeyB"` など。 -- 数字キーはコード `"Digit<number>"` です:` "Digit0"`, `"Digit1"` など。 -- 特別なキーは名前でコード化されています: `"Enter"`, `"Backspace"`, `"Tab"` など。 - -いくつかの広く知られているキーボードレイアウトがあり、仕様はそれぞれに対してキーコードを提供します。 - -より多くのコードについては [alphanumeric section of the spec](https://www.w3.org/TR/uievents-code/#key-alphanumeric-section) を見てください、もしくは上の [テストスタンド](#keyboard-test-stand) を試してみてください。 -``` - -```warn header="大文字小文字の問題: `\"KeyZ\"` です, `\"keyZ\"` ではありません" -明らかですが、それでも間違えることがあります。 - -ミスタイプを避けてください: `keyZ` ではなく `KeyZ` です。`event.code=="keyZ"` のようなチェックは機能しません: `"Key"` の最初の文字は大文字でなければなりません。 -``` - -もし仮に、キーが文字を持たないとどうなるでしょう?例えば、`key:Shift` or `key:F1` などです。これらのキーの場合、`event.key` は `event.code` とほぼ同じです。: - - -| キー | `event.key` | `event.code` | -|--------------|-------------|--------------| -| `key:F1` |`F1` |`F1` | -| `key:Backspace` |`Backspace` |`Backspace` | -| `key:Shift`|`Shift` |`ShiftRight` or `ShiftLeft` | - -`event.code` は正確にどのキーが押されたかを指定することに注意してください。例えば、ほとんどのキーボードは2つの `key:Shift` キーを持っています: 左側と右側です。`event.code` は厳密にどちらが押されたのかを示し、`event.key` はキーの "意味" に対して責任を持っています: それは何か("Shift")です。 - -私たちはホットキーを処理したいとしましょう: `key:Ctrl+Z` (Mac だと`key:Cmd+Z`)。多くのテキストエディタは "元に戻す" アクションをフックします。`keydown` にリスナーを設定して、どのキーが押されたか確認することができます -- ホットキーを検出するために。 - -問題に答えてください -- このようなリスナーにおいて、`event.key` または `event.code` どちらの値をチェックするべきでしょうか? - -立ち止まって答えてみてください。 - -もう決めましたか? - -あなたが理解しているのであれば、答えはもちろん `event.code` です。ここでは `event.key` は欲しくありません。`event.key` は言語、もしくは `CapsLock` の有効化に依存して変わる可能性があります。`event.code` は厳密にキーに紐付いているので、ここでは次のように書けます: - -```js run -document.addEventListener('keydown', function(event) { - if (event.code == 'KeyZ' && (event.ctrlKey || event.metaKey)) { - alert('Undo!') - } -}); -``` - -## 自動繰り返し - -もしキーが長時間押されていると、繰り返しを始めます: `keydown` は何度もトリガされ、その後キーが離されたとき、最終的に `keyup` を得ます。そのため、多くの `keydown` と単一の `keyup` をもつのは普通なことです。 - -すべての繰り返しキーに対して、イベントオブジェクトは `event.repeat` プロパティが `true` に設定されています。 - - -## デフォルトアクション - -キーボードによって開始され得ることはたくさんあるので、デフォルトアクションさまざまです。 - -例えば: - -- 画面に文字が現れる(もっとも明白な結果) -- 文字が削除される(`key:Delete` キー) -- ページがスクロールされる(`key:PageDown` キー) -- ブラウザが "保存" ダイアログを開く (`key:Ctrl+S`) -- などなど - -`keydown` のデフォルトアクションを防ぐことは、OSベースの特別なキーを除き、それらのほとんどを取り消すことが可能です。例えば、Windows の `key:Alt+F4` は現在のブラウザウィンドウを閉じます。そして、JavaScript でデフォルトアクションを防ぐことでそれを止める方法はありません。 - -別の例で、下記の `<input>` は電話番号を期待しているので、数値, `+`, `()` または `-` 以外は許可しません。: - -```html autorun height=60 run -<script> -function checkPhoneKey(key) { - return (key >= '0' && key <= '9') || key == '+' || key == '(' || key == ')' || key == '-'; -} -</script> -<input *!*onkeydown="return checkPhoneKey(event.key)"*/!* placeholder="Phone, please" type="tel"> -``` - -`key:Backspace`, `key:Left`, `key:Right`, `key:Ctrl+V` のような特別なキーはインプットでは動作しないことに注意してください。これは厳密なフィルタ `checkPhoneKey` の副作用です。 - -少しだけ緩めましょう。: - - -```html autorun height=60 run -<script> -function checkPhoneKey(key) { - return (key >= '0' && key <= '9') || key == '+' || key == '(' || key == ')' || key == '-' || - key == 'ArrowLeft' || key == 'ArrowRight' || key == 'Delete' || key == 'Backspace'; -} -</script> -<input onkeydown="return checkPhoneKey(event.key)" placeholder="Phone, please" type="tel"> -``` - -今は矢印や削除も動作します。 - -...しかし、まだマウスや右クリック+貼り付けを使用してどのような値も入力することができます。なので、フィルタは 100% 信頼はできません。ただ、殆どの場合では動作するのでこのようにすることはできます。もしくは、代わりのアプローチは `input` イベントを追跡することです -- これはすべての変更のあとにトリガされます。そこでは新しい値をチェックし、それが無効であるときには強調/変更することができます。 - -## レガシー - -過去、`keypress` イベントや、`keyCode`, `charCode`, `which` と言ったイベントオブジェクトのプロパティがありました。 - -そこにはブラウザの非互換性が非常に多く、仕様の開発者はそれらのすべてを非推奨にすることに決めました。ブラウザがそれらをサポートし続けるので、古いコードはまだ動作しますが、それらをもう使用する必要は全くありません。 - -このチャプターに、それらの詳しい説明があった時期もありました。しかし、今のところ、私たちはそれらを忘れても問題ありません。 - -## サマリ - -キーを押すと、キーに応じたキーボードイベントが常に生成されます。唯一の例外は、ノートパソコンのキーボードに表示される `key:Fn` キーです。 それはOSよりも低いレベルで実装されることが多いため、キーボードイベントはありません。 - -キーボードイベント: - -- `keydown` -- キーを押したとき(長押しの場合は自動的に繰り返されます) -- `keyup` -- キーを離したとき - -主なキーボードイベントのプロパティ: - -- `code` -- "キーコード" (`"KeyA"`, `"ArrowLeft"` など)。キーボード上のキーの物理的な位置に固有です。 -- `key` -- 文字 (`"A"`, `"a"` など)。非文字のキーの場合は通常 `code` と同じ値を持っています。 - -過去、キーボードイベントはフォームフィールドで、ユーザ入力を追跡するために使われていました。しかし、入力は様々な方法で行われる可能性があるため、それは信頼できません。任意の入力を処理するために `input` と `change` イベントがあります (これらについてはチャプター <info:events-change-input> で後ほど説明します)。これらは任意の入力後にトリガされ、マウスや音声認識なども含みます。 - -本当にキーボードが必要なときにキーボードイベントを使うべきです。例えば、ホットキーや特別なキーに反応するため、などです。 diff --git a/2-ui/3-event-details/5-keyboard-events/2-check-sync-keydown/solution.md b/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.md similarity index 100% rename from 2-ui/3-event-details/5-keyboard-events/2-check-sync-keydown/solution.md rename to 2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.md diff --git a/2-ui/3-event-details/5-keyboard-events/2-check-sync-keydown/solution.view/index.html b/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.view/index.html similarity index 100% rename from 2-ui/3-event-details/5-keyboard-events/2-check-sync-keydown/solution.view/index.html rename to 2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.view/index.html diff --git a/2-ui/3-event-details/5-keyboard-events/2-check-sync-keydown/task.md b/2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/task.md similarity index 100% rename from 2-ui/3-event-details/5-keyboard-events/2-check-sync-keydown/task.md rename to 2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/task.md diff --git a/2-ui/3-event-details/7-keyboard-events/article.md b/2-ui/3-event-details/7-keyboard-events/article.md new file mode 100644 index 0000000000..66057f341d --- /dev/null +++ b/2-ui/3-event-details/7-keyboard-events/article.md @@ -0,0 +1,193 @@ +# キーボード: keydown と keyup + +キーボードを学ぶ前に、現代のデバイスでは "何かを入力する" ために他の方法があることに留意してください。例えば、人々は音声認識(特にモバイルデバイスで)や、マウスによるコピーペーストを使います。 + +したがって、`<input>` フィールドへの任意の入力を追跡したい場合、キーボードイベントだけでは十分ではありません。`<input>` フィールドのどのような方法での変更も処理するために `input` と言う名前の別のイベントがあります。そしてこのようなタスクに対する良い選択である場合があります。それらに関しては、チャプター <info:events-change-input> で後ほど説明します。 + +キーボードイベントは、キーボードアクション(仮想キーボードも含む)を処理したいときに使われるべきです。例えば、矢印キー `key:Up` や `key:Down`、またはホットキー (キーの組み合わせを含む)に反応するために使います。 + + +## テストスタンド [#keyboard-test-stand] + +```offline +キーボードイベントをより良く理解するために、[テストスタンド](sandbox:keyboard-dump)が使えます。 +``` + +```online +キーボードイベントをよりよく理解するために、下のテストスタンドがあります。 + +テキストフィールドの中で、様々なキーの組み合わせを試してみてください。 + +[codetabs src="keyboard-dump" height=480] +``` + + +## Keydown と keyup + +`keydown` イベントはキーが押された時に、そして `keyup` はそれが離されたときに発生します。 + +### event.code と event.key + +イベントオブジェクトの `key` プロパティは、イベントオブジェクトの `code` プロパティが "物理的なキーコード" を取得できる一方、文字を取得することができます。 + +例えば、同じキー `key:Z` が `Shift` あり / なし で押される場合があります。それは2つの異なる文字を返します: 小文字の `z` と大文字の `Z` です。 + +`event.key` はまさに文字であり、それは異なるでしょう。しかし、`event.code` は同じです: + + +| キー | `event.key` | `event.code` | +|--------------|-------------|--------------| +| `key:Z` |`z` (小文字) |`KeyZ` | +| `key:Shift+Z`|`Z` (大文字) |`KeyZ` | + + +ユーザが異なる言語で作業する場合、別の言語に切り替えると、`"Z"` の代わりに全く違う文字になります。これは `event.key` の文字になりますが、その一方で `event.code` は常に同じ `"KeyZ"` です。 + +```smart header="\"KeyZ\" とその他のキーコード" +すべてのキーは、キーボード上の位置に応じたコードを持っています。キーコードは[UI イベントコード仕様](https://www.w3.org/TR/uievents-code/)で記載されています。 + +例えば: +- 文字キーはコード `"Key<letter>"` です: `"KeyA"`, `"KeyB"` など。 +- 数字キーはコード `"Digit<number>"` です:` "Digit0"`, `"Digit1"` など。 +- 特別なキーは名前でコード化されています: `"Enter"`, `"Backspace"`, `"Tab"` など。 + +いくつかの広く知られているキーボードレイアウトがあり、仕様はそれぞれに対してキーコードを提供します。 + +より多くのコードについては [alphanumeric section of the spec](https://www.w3.org/TR/uievents-code/#key-alphanumeric-section) を見てください、もしくは上の [テストスタンド](#keyboard-test-stand) を試してみてください。 +``` + +```warn header="大文字小文字の問題: `\"KeyZ\"` です, `\"keyZ\"` ではありません" +明らかですが、それでも間違えることがあります。 + +ミスタイプを避けてください: `keyZ` ではなく `KeyZ` です。`event.code=="keyZ"` のようなチェックは機能しません: `"Key"` の最初の文字は大文字でなければなりません。 +``` + +もし仮に、キーが文字を持たないとどうなるでしょう?例えば、`key:Shift` or `key:F1` などです。これらのキーの場合、`event.key` は `event.code` とほぼ同じです。: + + +| キー | `event.key` | `event.code` | +|--------------|-------------|--------------| +| `key:F1` |`F1` |`F1` | +| `key:Backspace` |`Backspace` |`Backspace` | +| `key:Shift`|`Shift` |`ShiftRight` or `ShiftLeft` | + +`event.code` は正確にどのキーが押されたかを特定することに注意してください。例えば、ほとんどのキーボードは2つの `key:Shift` キーを持っています: 左側と右側です。`event.code` は厳密にどちらが押されたのかを示し、`event.key` はキーの "意味" に対して責任を持っています: それは何か("Shift")です。 + +ここで、ホットキーを処理したいとしましょう: `key:Ctrl+Z` (Mac だと`key:Cmd+Z`)。多くのテキストエディタは "元に戻す" アクションをフックします。`keydown` にリスナーを設定して、どのキーが押されたか確認することができます -- ホットキーを検出するために。 + +ここにはジレンマがあります。:このようなリスナーにおいては、`event.key` または `event.code` どちらの値をチェックするべきでしょうか? + +`event.key` の場合、値は文字であり、言語によって異なります。訪問者が OS で複数の言語をもち、切り替えた場合、同じキーで異なる文字になります。したがって、常に同じである `event.code` をチェックするのが理にかなっています。 + +次のようになります: + +```js run +document.addEventListener('keydown', function(event) { + if (event.code == 'KeyZ' && (event.ctrlKey || event.metaKey)) { + alert('Undo!') + } +}); +``` + +その一方で、`event.code` を利用した場合の問題もあります。異なるキーボードレイアウトの場合、同じキーが異なる文字を持つ場合があります。 + +例えば、ここに US レイアウト ("QWERTY") とその下のドイツ語のレイアウト("QWERTZ")です(Wikipedia より) + +![](us-layout.svg) + +![](german-layout.svg) + +同じキーに対して、US レイアウトは "Z" である一方、ドイツ語レイアウトは "Y" (文字が入れ替わっています) + +文字通り、`key:Y` を押すと、ドイツ語のレイアウトを持つ人々の `event.code` は `KeyZ` と等しくなります。 + +もしコードで `event.code == 'KeyZ'` というチェックをしている場合、ドイツ語レイアウトの人々は、`key:Y` を押すことでこの評価が通ります。 + +これはとても奇妙に思えますが、そのように動作します。[仕様](https://www.w3.org/TR/uievents-code/#table-key-code-alphanumeric-writing-system) でこのような振る舞いについて明示的に言及されています。 + +したがって、`event.code` は予期しないレイアウトの場合、間違った文字と一致する可能性があります。異なるレイアウトにおいて、同じ文字が異なる物理キーに割りあてられ、異なるコードに繋がる場合があります。幸いなことに、これはいくつかのコードでのみ起こりえます, 例: `keyA`, `keyQ`, `keyZ` (これまで見てきたように)。また、`Shift` のような特別なキーでは発生しません。[仕様](https://www.w3.org/TR/uievents-code/#table-key-code-alphanumeric-writing-system)で一覧を見ることができます。 + +レイアウトに依存する文字を確実に追跡するには、`event.key` のほうが適している場合があります。 + +一方、`event.code` は訪問者が言語を変えたとしても、物理キーにバインドされて、常に同じままであるという利点があります。したがって、これに依存するホットキーは、言語が切り替わった場合でもうまく機能します。 + +レイアウトに依存するキーを扱いたいたいですか? その場合は `event.key` がよいです。 + +あるいは、言語を切り替えた後でもホットキーを機能させたいですか?それなら `event.code` のほうがよいかもしれません。 + +## 自動繰り返し + +もしキーが長時間押されていると、繰り返しを始めます: `keydown` は何度もトリガされ、その後キーが離されたとき、最終的に `keyup` を得ます。そのため、多くの `keydown` と単一の `keyup` をもつのは普通なことです。 + +すべての繰り返しキーに対して、イベントオブジェクトは `event.repeat` プロパティが `true` に設定されています。 + + +## デフォルトアクション + +キーボードによって開始され得ることはたくさんあるので、デフォルトアクションさまざまです。 + +例えば: + +- 画面に文字が現れる(もっとも明白な結果) +- 文字が削除される(`key:Delete` キー) +- ページがスクロールされる(`key:PageDown` キー) +- ブラウザが "保存" ダイアログを開く (`key:Ctrl+S`) +- などなど + +`keydown` のデフォルトアクションを防ぐことは、OSベースの特別なキーを除き、それらのほとんどを取り消すことが可能です。例えば、Windows の `key:Alt+F4` は現在のブラウザウィンドウを閉じます。そして、JavaScript でデフォルトアクションを防ぐことでそれを止める方法はありません。 + +別の例で、下記の `<input>` は電話番号を期待しているので、数値, `+`, `()` または `-` 以外は許可しません。: + +```html autorun height=60 run +<script> +function checkPhoneKey(key) { + return (key >= '0' && key <= '9') || key == '+' || key == '(' || key == ')' || key == '-'; +} +</script> +<input *!*onkeydown="return checkPhoneKey(event.key)"*/!* placeholder="Phone, please" type="tel"> +``` + +`key:Backspace`, `key:Left`, `key:Right`, `key:Ctrl+V` のような特別なキーはインプットでは動作しないことに注意してください。これは厳密なフィルタ `checkPhoneKey` の副作用です。 + +少しだけ緩めましょう。: + + +```html autorun height=60 run +<script> +function checkPhoneKey(key) { + return (key >= '0' && key <= '9') || key == '+' || key == '(' || key == ')' || key == '-' || + key == 'ArrowLeft' || key == 'ArrowRight' || key == 'Delete' || key == 'Backspace'; +} +</script> +<input onkeydown="return checkPhoneKey(event.key)" placeholder="Phone, please" type="tel"> +``` + +今は矢印や削除も動作します。 + +...しかし、まだマウスや右クリック+貼り付けを使用してどのような値も入力することができます。なので、フィルタは 100% 信頼はできません。ただ、殆どの場合では動作するのでこのようにすることはできます。もしくは、代わりのアプローチは `input` イベントを追跡することです -- これはすべての変更のあとにトリガされます。そこでは新しい値をチェックし、それが無効であるときには強調/変更することができます。 + +## レガシー + +過去、`keypress` イベントや、`keyCode`, `charCode`, `which` と言ったイベントオブジェクトのプロパティがありました。 + +そこにはブラウザの非互換性が非常に多く、仕様の開発者はそれらのすべてを非推奨にすることに決めました。ブラウザがそれらをサポートし続けるので、古いコードはまだ動作しますが、それらをもう使用する必要は全くありません。 + +このチャプターに、それらの詳しい説明があった時期もありました。しかし、今のところ、私たちはそれらを忘れても問題ありません。 + +## サマリ + +キーを押すと、キーに応じたキーボードイベントが常に生成されます。唯一の例外は、ノートパソコンのキーボードに表示される `key:Fn` キーです。 それはOSよりも低いレベルで実装されることが多いため、キーボードイベントはありません。 + +キーボードイベント: + +- `keydown` -- キーを押したとき(長押しの場合は自動的に繰り返されます) +- `keyup` -- キーを離したとき + +主なキーボードイベントのプロパティ: + +- `code` -- "キーコード" (`"KeyA"`, `"ArrowLeft"` など)。キーボード上のキーの物理的な位置に固有です。 +- `key` -- 文字 (`"A"`, `"a"` など)。非文字のキーの場合は通常 `code` と同じ値を持っています。 + +過去、キーボードイベントはフォームフィールドで、ユーザ入力を追跡するために使われていました。しかし、入力は様々な方法で行われる可能性があるため、それは信頼できません。任意の入力を処理するために `input` と `change` イベントがあります (これらについてはチャプター <info:events-change-input> で後ほど説明します)。これらは任意の入力後にトリガされ、マウスや音声認識なども含みます。 + +本当にキーボードが必要なときにキーボードイベントを使うべきです。例えば、ホットキーや特別なキーに反応するため、などです。 diff --git a/2-ui/3-event-details/7-keyboard-events/german-layout.svg b/2-ui/3-event-details/7-keyboard-events/german-layout.svg new file mode 100644 index 0000000000..7ac9a4008b --- /dev/null +++ b/2-ui/3-event-details/7-keyboard-events/german-layout.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="600" height="200" viewBox="0 0 600 200"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="Group" transform="translate(.918 .953)"><path id="rect2186" fill="#FFF" stroke="#181717" d="M.08.083h39.933V39.46H.08z"/><path id="rect2218" fill="#FFF" stroke="#181717" d="M40.013.083h39.934V39.46H40.013z"/><path id="rect2222" fill="#FFF" stroke="#181717" d="M79.947.083h39.933V39.46H79.947z"/><path id="rect2228" fill="#FFF" stroke="#181717" d="M118.882.083h39.933V39.46h-39.933z"/><path id="rect2230" fill="#FFF" stroke="#181717" d="M158.815.083h39.934V39.46h-39.934z"/><path id="rect2232" fill="#FFF" stroke="#181717" d="M198.749.083h39.933V39.46H198.75z"/><path id="rect2234" fill="#FFF" stroke="#181717" d="M239.68.083h39.934V39.46h-39.933z"/><path id="rect2236" fill="#FFF" stroke="#181717" d="M279.614.083h39.933V39.46h-39.933z"/><path id="rect2238" fill="#FFF" stroke="#181717" d="M318.55.083h39.933V39.46h-39.934z"/><path id="rect2240" fill="#FFF" stroke="#181717" d="M358.483.083h39.933V39.46h-39.933z"/><path id="rect2242" fill="#FFF" stroke="#181717" d="M398.416.083h39.933V39.46h-39.933z"/><path id="rect2244" fill="#FFF" stroke="#181717" d="M438.35.083h39.933V39.46h-39.934z"/><path id="rect2246" fill="#FFF" stroke="#181717" d="M478.283.083h39.933V39.46h-39.933z"/><path id="rect2248" fill="#D1CFCD" stroke="#181717" d="M517.218.083h81.864V39.46h-81.864z"/><path id="rect2250" fill="#D1CFCD" stroke="#181717" d="M.08 39.461h59.9V78.84H.08z"/><path id="rect2252" fill="#FFF" stroke="#181717" d="M59.98 39.461h39.933V78.84H59.98z"/><path id="rect2254" fill="#FFF" stroke="#181717" d="M98.915 39.461h39.934V78.84H98.915z"/><path id="rect2256" fill="#FFF" stroke="#181717" d="M138.849 39.461h39.933V78.84h-39.933z"/><path id="rect2258" fill="#FFF" stroke="#181717" d="M178.782 39.461h39.933V78.84h-39.933z"/><path id="rect2262" fill="#FFF" stroke="#181717" d="M219.714 39.461h39.933V78.84h-39.933z"/><path id="rect2264" fill="#FFF" stroke="#181717" d="M259.647 39.461h39.934V78.84h-39.934z"/><path id="rect2266" fill="#FFF" stroke="#181717" d="M298.582 39.461h39.934V78.84h-39.934z"/><path id="rect2270" fill="#FFF" stroke="#181717" d="M338.516 39.461h39.933V78.84h-39.933z"/><path id="rect2272" fill="#FFF" stroke="#181717" d="M378.45 39.461h39.933V78.84h-39.934z"/><path id="rect2274" fill="#FFF" stroke="#181717" d="M418.383 39.461h39.933V78.84h-39.933z"/><path id="rect2278" fill="#FFF" stroke="#181717" d="M458.316 39.461h39.934V78.84h-39.934z"/><path id="rect2280" fill="#FFF" stroke="#181717" d="M497.251 39.461h39.934V78.84H497.25z"/><path id="rect2286" fill="#D1CFCD" stroke="#181717" d="M.08 79.876h69.883v39.378H.08z"/><path id="rect2292" fill="#FFF" stroke="#181717" d="M69.963 79.876h39.934v39.378H69.963z"/><path id="rect2296" fill="#FFF" stroke="#181717" d="M109.897 79.876h39.933v39.378h-39.933z"/><path id="rect2298" fill="#FFF" stroke="#181717" d="M149.83 79.876h39.934v39.378H149.83z"/><path id="rect2300" fill="#FFF" stroke="#181717" d="M188.765 79.876H228.7v39.378h-39.934z"/><path id="rect2302" fill="#FFF" stroke="#181717" d="M228.699 79.876h39.933v39.378H228.7z"/><path id="rect2306" fill="#FFF" stroke="#181717" d="M268.632 79.876h39.934v39.378h-39.934z"/><path id="rect2308" fill="#FFF" stroke="#181717" d="M308.566 79.876h39.933v39.378h-39.933z"/><path id="rect2312" fill="#FFF" stroke="#181717" d="M348.5 79.876h39.933v39.378h-39.934z"/><path id="rect2314" fill="#FFF" stroke="#181717" d="M388.433 79.876h39.933v39.378h-39.933z"/><path id="rect2316" fill="#FFF" stroke="#181717" d="M428.366 79.876H468.3v39.378h-39.934z"/><path id="rect2318" fill="#FFF" stroke="#181717" d="M468.3 79.876h39.933v39.378H468.3z"/><path id="rect2320" fill="#D1CFCD" stroke="#181717" stroke-width="1" d="M547.5 79.358h-10.315V39.46h61.896v79.793h-51.656z"/><path id="rect2322" fill="#D1CFCD" stroke="#181717" stroke-width="1" d="M.08 119.254h49.917v39.378H.08z"/><path id="rect2324" fill="#FFF" stroke="#181717" d="M89.93 119.254h39.934v39.378H89.93z"/><path id="rect2326" fill="#FFF" stroke="#181717" d="M129.864 119.254h39.933v39.378h-39.933z"/><path id="rect2330" fill="#FFF" stroke="#181717" d="M169.797 119.254h39.933v39.378h-39.933z"/><path id="rect2334" fill="#FFF" stroke="#181717" d="M208.732 119.254h39.934v39.378h-39.934z"/><path id="rect2336" fill="#FFF" stroke="#181717" d="M248.666 119.254h39.933v39.378h-39.933z"/><path id="rect2338" fill="#FFF" stroke="#181717" d="M288.599 119.254h39.933v39.378H288.6z"/><path id="rect2340" fill="#FFF" stroke="#181717" d="M328.532 119.254h39.934v39.378h-39.934z"/><path id="rect2342" fill="#FFF" stroke="#181717" d="M368.466 119.254h39.933v39.378h-39.933z"/><path id="rect2344" fill="#FFF" stroke="#181717" d="M408.4 119.254h39.933v39.378h-39.934z"/><path id="rect2346" fill="#FFF" stroke="#181717" d="M448.333 119.254h39.933v39.378h-39.933z"/><path id="rect2348" fill="#D1CFCD" stroke="#181717" d="M488.266 119.254h110.816v39.378H488.266z"/><path id="rect2350" fill="#D1CFCD" stroke="#181717" d="M.08 158.632h59.9v39.378H.08z"/><path id="rect2352" fill="#D1CFCD" stroke="#181717" d="M537.185 158.632h61.897v39.378h-61.897z"/><path id="rect2354" fill="#D1CFCD" stroke="#181717" d="M98.915 158.632h59.9v39.378h-59.9z"/><path id="rect2360" fill="#D1CFCD" stroke="#181717" d="M398.416 158.632h59.9v39.378h-59.9z"/><path id="rect2362" fill="#FFF" stroke="#181717" d="M158.815 158.632h239.601v39.378h-239.6z"/><path id="rect2364" fill="#F6F3F1" stroke="#181717" d="M59.98 158.632h39.933v39.378H59.98z"/><path id="rect2366" fill="#F6F3F1" stroke="#181717" d="M458.316 158.632h39.934v39.378h-39.934z"/><path id="rect2368" fill="#F6F3F1" stroke="#181717" d="M497.251 158.632h39.934v39.378H497.25z"/><path id="text5091" fill="#181717" fill-rule="nonzero" d="M49.737 12.695l-.623-5.28V4.227h2.463v3.186l-.624 5.281h-1.216m-.591 3.266v-2.35h2.375v2.35h-2.375m.591 20.391v-7.798h-2.735v-1.6h.176c.997 0 1.722-.155 2.176-.463.458-.308.727-.823.807-1.545h1.832v11.406h-2.256"/><path id="text5127" fill="#181717" fill-rule="nonzero" d="M89.635 5.264h1.507v4.347h-1.507V5.264m-2.56 0h1.507v4.347h-1.507V5.264m-2.137 31.088c.01-.995.226-1.837.644-2.527.42-.694 1.163-1.397 2.23-2.107.163-.109.399-.26.707-.451 1.414-.897 2.12-1.76 2.12-2.59 0-.492-.148-.88-.447-1.166-.298-.285-.707-.428-1.225-.428-.565 0-1.002.177-1.311.53-.304.347-.456.844-.456 1.492v.093h-2.073c0-1.275.345-2.257 1.037-2.947.69-.689 1.675-1.034 2.952-1.034 1.157 0 2.081.314 2.773.941.69.622 1.036 1.452 1.036 2.488 0 .742-.18 1.385-.542 1.929-.361.544-1.06 1.164-2.097 1.858-.22.15-.523.348-.91.591-.985.628-1.54 1.104-1.665 1.431h5.104v1.897h-7.877"/><path id="text5139" fill="#181717" fill-rule="nonzero" d="M126.869 31.62h2.136c.005.62.155 1.087.45 1.402.296.31.734.465 1.314.465.559 0 .989-.144 1.29-.434.305-.294.458-.715.458-1.263 0-.547-.179-.96-.536-1.24-.353-.284-.878-.426-1.577-.426-.037 0-.094.003-.171.008a2.506 2.506 0 01-.164.008v-1.535h.241c.643 0 1.116-.124 1.422-.372.31-.253.466-.638.466-1.154 0-.429-.132-.767-.396-1.015-.259-.254-.614-.38-1.064-.38-.492 0-.873.145-1.142.434-.27.29-.404.7-.404 1.232v.093h-2.098c.026-1.137.36-2.007 1.002-2.612.648-.61 1.56-.914 2.735-.914 1.113 0 1.996.271 2.65.814.652.542.978 1.273.978 2.193 0 .485-.111.912-.334 1.278-.223.367-.557.67-1.002.907.57.248 1 .592 1.29 1.03.295.435.442.954.442 1.558 0 1.11-.365 1.992-1.095 2.643-.73.65-1.728.976-2.991.976-1.249 0-2.212-.315-2.89-.945-.674-.636-1.01-1.537-1.01-2.705v-.046"/><path id="text5145" fill="#181717" fill-rule="nonzero" d="M168.895 14.91c.468-.057.825-.21 1.07-.46.246-.25.369-.584.369-1 0-.37-.117-.674-.351-.914-.229-.244-.591-.434-1.088-.57v2.944m-1.02-5.341V6.914c-.445.052-.785.193-1.019.422-.234.224-.35.526-.35.906 0 .317.108.583.325.796.222.213.57.39 1.045.531m0 8.55V16.62c-1.291-.073-2.256-.414-2.896-1.023-.64-.609-.959-1.489-.959-2.64h2.296c.028.563.17 1.008.428 1.336.263.323.64.529 1.13.617v-3.233c-1.324-.307-2.266-.708-2.826-1.202-.56-.495-.84-1.171-.84-2.03 0-.953.332-1.72.994-2.304.668-.583 1.559-.887 2.673-.914v-.999h1.019v1c1.113.052 1.981.359 2.604.92.622.558.965 1.344 1.027 2.36h-2.33c-.04-.485-.168-.855-.385-1.11-.211-.26-.517-.406-.916-.437v2.842c1.41.401 2.404.86 2.98 1.375.577.515.866 1.194.866 2.038 0 .994-.337 1.788-1.011 2.381-.668.594-1.613.94-2.835 1.039v1.483h-1.02m1.157 14.095v-4.787l-3.332 4.787h3.332m-.052 4.138V33.94h-5.173v-1.967l4.522-6.481h3.041v6.668h1.422v1.78h-1.422v2.413h-2.39"/><path id="text5151" fill="#181717" fill-rule="nonzero" d="M212.846 12.99c0 .356.11.656.33.9.22.239.493.358.816.358.314 0 .583-.122.809-.366.225-.25.338-.547.338-.892 0-.34-.113-.63-.338-.868a1.048 1.048 0 00-.809-.367c-.323 0-.595.12-.816.359-.22.233-.33.525-.33.876m-1.595 0c0-.829.264-1.527.793-2.095a2.558 2.558 0 011.948-.852c.755 0 1.396.29 1.926.868.534.573.8 1.266.8 2.079 0 .818-.266 1.519-.8 2.103-.534.579-1.176.868-1.926.868-.77 0-1.418-.287-1.948-.86-.529-.574-.793-1.277-.793-2.111m-4.27 2.971l6.276-11.733h1.183l-6.276 11.733h-1.184m-.646-8.762c0 .35.107.645.323.884.22.24.492.359.816.359.318 0 .588-.12.808-.359.226-.244.338-.539.338-.884 0-.34-.112-.63-.338-.868a1.048 1.048 0 00-.808-.367c-.324 0-.595.12-.816.359a1.243 1.243 0 00-.323.876m-1.595 0c0-.828.264-1.53.794-2.103.529-.579 1.175-.868 1.94-.868.754 0 1.396.292 1.925.876.535.579.802 1.277.802 2.095 0 .818-.267 1.516-.802 2.095-.529.573-1.17.86-1.925.86-.77 0-1.419-.284-1.948-.852-.524-.568-.786-1.27-.786-2.103m2.102 25.76h2.058c.034.5.188.887.463 1.163.274.27.642.406 1.102.406.569 0 1.002-.18 1.301-.541.304-.361.456-.88.456-1.554 0-.632-.155-1.128-.463-1.49-.309-.366-.735-.549-1.28-.549-.303 0-.57.067-.8.2-.23.132-.427.329-.588.589l-1.896-.12.683-6.117h6.049v1.928h-4.564l-.272 2.286c.23-.197.495-.343.794-.438.298-.101.632-.152 1-.152 1.048 0 1.893.345 2.535 1.036.647.69.97 1.595.97 2.716 0 1.227-.355 2.206-1.066 2.94-.71.727-1.663 1.09-2.859 1.09-1.088 0-1.955-.3-2.602-.9-.641-.605-.982-1.436-1.021-2.493"/><path id="path3073" fill="#181717" fill-rule="nonzero" d="M248.136 10.477l-.576.414c-.232.16-.406.354-.523.58-.116.226-.174.48-.174.76 0 .427.146.786.44 1.077.293.291.66.437 1.099.437.323 0 .631-.066.925-.196.298-.136.573-.331.826-.587l-2.017-2.485m.607-2.372l.265-.188c.243-.165.422-.351.538-.557.117-.206.175-.442.175-.708 0-.256-.081-.459-.243-.61-.156-.15-.371-.225-.644-.225-.278 0-.498.077-.66.233-.161.15-.242.356-.242.617 0 .14.038.294.113.46.081.165.195.341.342.527l.356.451m-1.706 1.002l-.44-.572a3.576 3.576 0 01-.492-.844 2.442 2.442 0 01-.144-.835c0-.803.258-1.44.773-1.913.52-.477 1.226-.715 2.115-.715.854 0 1.529.218 2.024.655.495.437.743 1.026.743 1.77 0 .562-.147 1.066-.44 1.513-.288.441-.743.858-1.364 1.25l1.6 1.957c.15-.246.27-.522.355-.828.091-.306.155-.648.19-1.024h1.97a6.483 6.483 0 01-.408 1.814 5.604 5.604 0 01-.895 1.529l2.032 2.454h-2.578l-.758-.926a4.741 4.741 0 01-1.47.926 4.572 4.572 0 01-1.668.309c-1.031 0-1.875-.304-2.532-.911-.652-.607-.978-1.383-.978-2.327 0-.702.182-1.307.546-1.814.364-.507.97-.996 1.82-1.468"/><path id="text5163" fill="#181717" fill-rule="nonzero" d="M288.564 36.352h-2.353c.084-1.6.454-3.187 1.11-4.76.66-1.574 1.602-3.12 2.824-4.64h-5.54v-1.999h7.987v1.762c-1.238 1.42-2.19 2.923-2.856 4.507-.661 1.585-1.052 3.295-1.172 5.13"/><path id="path3094" fill="#181717" fill-rule="nonzero" d="M506.735 48.674l-1.417 2.151-1.368-1.032 1.546-2.091-2.255-.677.524-1.761 2.165.832v-2.49h1.594v2.49l2.166-.815.54 1.761-2.255.66 1.53 2.073-1.4 1.085-1.37-2.186"/><path id="path3092" fill="#181717" fill-rule="nonzero" d="M325.79 31.784c0 .541.15.959.45 1.252.304.294.738.44 1.302.44.537 0 .959-.149 1.264-.447.31-.3.465-.714.465-1.245 0-.51-.158-.92-.473-1.228-.315-.31-.734-.464-1.256-.464s-.946.157-1.272.471c-.32.315-.48.722-.48 1.221m.178-4.745c0 .428.134.758.403.99.269.226.654.34 1.155.34.502 0 .887-.116 1.156-.348.274-.232.41-.56.41-.982 0-.412-.139-.736-.418-.973-.28-.237-.662-.356-1.148-.356-.475 0-.855.121-1.14.363-.279.242-.418.564-.418.966m-.799 2.149c-.465-.258-.801-.56-1.008-.905-.207-.345-.31-.785-.31-1.321 0-.948.326-1.692.977-2.233.651-.541 1.55-.812 2.698-.812 1.158 0 2.063.27 2.714.812.657.535.985 1.28.985 2.233 0 .51-.111.953-.333 1.33-.223.37-.556.669-1 .896.542.252.95.602 1.224 1.05.274.444.411.985.411 1.624 0 1.076-.356 1.921-1.07 2.534-.708.614-1.685.92-2.93.92-1.262 0-2.242-.304-2.94-.912-.697-.608-1.046-1.455-1.046-2.542 0-.634.129-1.165.387-1.592.264-.433.678-.794 1.241-1.082"/><path id="path3112" fill="#181717" fill-rule="nonzero" d="M330.53 5.264c-.81 1.15-1.412 2.32-1.806 3.51a11.87 11.87 0 00-.59 3.748c0 1.302.197 2.549.59 3.74.394 1.19.995 2.36 1.805 3.51h-1.857c-1.028-1.216-1.808-2.437-2.342-3.663-.528-1.231-.793-2.427-.793-3.587 0-1.16.265-2.356.793-3.587.534-1.231 1.314-2.455 2.342-3.67h1.857"/><path id="path3110" fill="#181717" fill-rule="nonzero" d="M369.808 27.766c0-.634-.18-1.134-.537-1.5-.353-.37-.835-.556-1.447-.556-.578 0-1.028.178-1.352.533-.318.356-.476.853-.476 1.492 0 .597.167 1.064.502 1.399.335.334.806.502 1.412.502.595 0 1.06-.165 1.395-.495.335-.33.503-.788.503-1.375m.043 2.882c-.289.258-.63.451-1.022.58a4.243 4.243 0 01-1.326.193c-1.219 0-2.195-.327-2.928-.981-.734-.655-1.1-1.53-1.1-2.628 0-1.164.398-2.105 1.195-2.82.797-.717 1.848-1.075 3.154-1.075 1.513 0 2.663.466 3.448 1.399.792.927 1.187 2.282 1.187 4.065 0 1.916-.421 3.385-1.265 4.405-.843 1.02-2.056 1.53-3.639 1.53-1.092 0-1.972-.252-2.642-.757-.67-.51-1.037-1.203-1.1-2.08l2.503.009c.058.355.214.62.468.796.254.175.612.262 1.074.262.624 0 1.1-.242 1.43-.726.33-.484.517-1.208.563-2.172"/><path id="path3121" fill="#181717" fill-rule="nonzero" d="M364.473 5.264h1.875c1.02 1.211 1.796 2.432 2.324 3.663.528 1.227.792 2.425.792 3.595 0 1.165-.264 2.363-.792 3.594-.528 1.232-1.303 2.45-2.324 3.656h-1.875c.815-1.155 1.42-2.328 1.813-3.518.4-1.196.599-2.44.599-3.732 0-1.297-.2-2.541-.599-3.732-.393-1.19-.998-2.366-1.813-3.526"/><path id="path3119" fill="#181717" fill-rule="nonzero" d="M405.986 29.62c0 1.349.15 2.328.451 2.937.307.61.793.915 1.459.915.665 0 1.152-.308 1.458-.922.307-.615.46-1.592.46-2.93 0-1.343-.153-2.32-.46-2.929-.306-.61-.793-.914-1.458-.914-.666 0-1.152.305-1.459.914-.3.605-.451 1.581-.451 2.93m-2.578 0c0-1.907.37-3.333 1.11-4.278.747-.95 1.873-1.426 3.378-1.426 1.499 0 2.622.478 3.368 1.434.752.95 1.129 2.379 1.129 4.285 0 1.906-.374 3.33-1.12 4.27-.741.94-1.867 1.41-3.377 1.41-1.505 0-2.63-.473-3.377-1.418-.74-.945-1.111-2.371-1.111-4.278"/><path id="text5187" fill="#181717" fill-rule="nonzero" d="M453.324 136.87h7.987v1.58h-7.987v-1.58m1.802 14.508v-2.122h4.383v2.122h-4.383"/><path id="path2908" fill="#181717" fill-rule="nonzero" d="M506.976 65.368h1.515v4.328h3.735v1.69h-3.735v4.345h-1.515v-4.346h-3.735v-1.689h3.735v-4.328"/><path id="path2906" fill="#181717" fill-rule="nonzero" d="M403.408 12.951h8.985v1.64h-8.985v-1.64m0-3.542h8.985v1.64h-8.985v-1.64"/><g id="g3468" transform="translate(522.21 13.554)"><path id="path5439" stroke="#181717" stroke-width="3" d="M34.942 5.448H6.61"/><path id="path5441" fill="#181717" d="M10.219 10.79V.105L.329 5.448z"/></g><g id="g5459" transform="translate(10.063 48.788)"><path id="path5453" stroke="#181717" stroke-width="3" d="M25.624 5.699H6.669"/><path id="path5455" fill="#181717" d="M10.649 11.043V.356L.666 5.7z"/><path id="path5457" stroke="#181717" stroke-width="3" d="M.666.356v10.687"/></g><g id="g5464" transform="rotate(-180 18.01 35.275)"><path id="path5466" stroke="#181717" stroke-width="3" d="M25.624 5.699H6.669"/><path id="path5468" fill="#181717" d="M10.649 11.043V.356L.666 5.7z"/><path id="path5470" stroke="#181717" stroke-width="3" d="M.666.356v10.687"/></g><path id="text5474" fill="#181717" fill-rule="nonzero" d="M71.803 52.199l-1.188-1.172 1.264-1.326 1.219 1.203c.162-.309.286-.66.373-1.056a6.08 6.08 0 00.13-1.296c0-1.248-.278-2.217-.83-2.906-.554-.694-1.329-1.04-2.324-1.04-.985 0-1.751.344-2.3 1.032-.548.689-.822 1.66-.822 2.914 0 1.25.274 2.22.822 2.915.549.688 1.315 1.033 2.3 1.033.254 0 .493-.026.716-.077a2.86 2.86 0 00.64-.224m1.622 1.55c-.38.257-.825.452-1.333.585a6.39 6.39 0 01-1.645.2c-1.675 0-3.008-.539-3.998-1.618-.985-1.08-1.477-2.534-1.477-4.364 0-1.834.492-3.289 1.477-4.363.99-1.08 2.323-1.62 3.998-1.62 1.676 0 3.008.54 3.999 1.62.995 1.08 1.492 2.534 1.492 4.363 0 .802-.101 1.537-.304 2.205a5.234 5.234 0 01-.9 1.743l1.22 1.18-1.25 1.325-1.279-1.256"/><path id="text5482" fill="#181717" fill-rule="nonzero" d="M105.05 55.005l-3.14-11.399h2.337l1.876 8.04 1.585-8.04h2.404l1.585 8.04 1.875-8.04h2.315l-3.133 11.4h-2.151l-1.697-8.73-1.704 8.73h-2.151"/><path id="text5492" fill="#181717" fill-rule="nonzero" d="M144.839 55.005V43.606h8.692v1.981h-6.261v2.43h5.724v1.95h-5.724V52.9h6.554v2.105h-8.985"/><path id="text5500" fill="#181717" fill-rule="nonzero" d="M187.015 48.59h2.634c.573 0 .992-.121 1.259-.364.271-.242.407-.624.407-1.145 0-.495-.131-.87-.393-1.122-.261-.258-.65-.387-1.169-.387h-2.738v3.018m-2.243 6.415V43.606h5.284c1.19 0 2.07.266 2.643.797.572.532.858 1.344.858 2.438 0 .696-.14 1.277-.422 1.741a2.108 2.108 0 01-1.199.952c.474.175.81.444 1.007.805.202.36.318.923.348 1.687l.044 1.346v.047c.015.68.156 1.093.422 1.238v.348H191.3a2.318 2.318 0 01-.185-.588 6.4 6.4 0 01-.074-.843l-.03-1.2c-.024-.707-.153-1.184-.385-1.432-.227-.247-.639-.371-1.236-.371h-2.375v4.434h-2.243"/><path id="text5504" fill="#181717" fill-rule="nonzero" d="M228.05 55.005v-9.364h-3.345v-2.035h8.986v2.035h-3.322v9.364h-2.319"/><path id="text5518" fill="#181717" fill-rule="nonzero" d="M101.245 134.798H98.96v-4.249l-4.037-7.15h2.8l2.376 4.914 2.196-4.914h2.611l-3.66 7.15v4.249"/><path id="text5522" fill="#181717" fill-rule="nonzero" d="M304.572 43.606h2.33v6.939c0 .856.173 1.482.518 1.877.345.39.894.586 1.645.586.761 0 1.315-.196 1.66-.586.35-.39.525-1.016.525-1.877v-6.939h2.307v7.187c0 1.356-.385 2.397-1.157 3.123-.767.726-1.873 1.09-3.32 1.09-1.457 0-2.574-.361-3.35-1.082-.772-.726-1.158-1.77-1.158-3.131v-7.187"/><path id="text5526" fill="#181717" fill-rule="nonzero" d="M347.5 55.005V43.606h1.998v11.4H347.5"/><path id="text5530" fill="#181717" fill-rule="nonzero" d="M384.8 48.788c0 1.298.274 2.307.823 3.028.549.716 1.316 1.074 2.303 1.074.996 0 1.772-.358 2.326-1.074.554-.72.831-1.73.831-3.028s-.277-2.305-.831-3.021c-.554-.721-1.33-1.082-2.326-1.082-.987 0-1.754.358-2.303 1.074-.55.716-.824 1.725-.824 3.029m-2.356 0c0-1.907.493-3.419 1.48-4.535.99-1.122 2.325-1.683 4.003-1.683s3.012.56 4.004 1.683c.996 1.121 1.494 2.633 1.494 4.535 0 1.901-.498 3.413-1.494 4.535-.992 1.121-2.326 1.682-4.004 1.682-1.678 0-3.013-.56-4.004-1.682-.986-1.122-1.48-2.634-1.48-4.535"/><path id="text5534" fill="#181717" fill-rule="nonzero" d="M425.808 48.884h2.338c.633 0 1.093-.129 1.38-.387.288-.263.432-.684.432-1.261 0-.542-.141-.952-.423-1.23-.282-.28-.703-.419-1.261-.419h-2.466v3.297m-.016 2.066v4.055h-2.418V43.606h5.227c1.234 0 2.168.312 2.8.937.64.619.958 1.53.958 2.731 0 1.177-.31 2.085-.933 2.724-.623.635-1.511.952-2.665.952h-2.969"/><path id="path3102" fill="#C9DCEA" fill-rule="nonzero" d="M311.56 21.86v1.6c-.049 0-.118-.003-.206-.008a3.788 3.788 0 00-.182-.007c-.556 0-.933.1-1.131.298-.193.194-.29.608-.29 1.24v1.838c0 .755-.124 1.291-.371 1.608-.248.316-.694.543-1.339.68.645.139 1.09.363 1.339.675.247.311.371.844.371 1.6v1.845c0 .628.097 1.038.29 1.232.192.194.57.291 1.131.291.033 0 .094-.002.182-.007s.157-.008.207-.008v1.6c-.077 0-.185.003-.322.008-.138.005-.24.007-.306.007-.551 0-1.01-.03-1.38-.092a3.388 3.388 0 01-.942-.283 1.495 1.495 0 01-.67-.704c-.126-.291-.19-.799-.19-1.524V31.82c0-.7-.14-1.194-.42-1.485-.281-.296-.755-.444-1.422-.444-.033 0-.088.002-.165.007a2.87 2.87 0 01-.173.008v-1.6c.038 0 .096.003.173.008.077.005.132.007.165.007.661 0 1.132-.148 1.413-.444.287-.296.43-.796.43-1.5v-1.914c0-.73.063-1.243.19-1.539.132-.296.355-.53.67-.704.258-.128.572-.222.941-.284a8.644 8.644 0 011.686-.084c.137.005.245.008.322.008"/><path id="text5546" fill="#C9DCEA" fill-rule="nonzero" d="M347.5 22.88h3.994v1.62h-1.858v11.27h1.858v1.619h-3.993V22.88"/><path id="path3106" fill="#C9DCEA" fill-rule="nonzero" d="M425.371 21.845h.562c.606 0 1.093.03 1.462.092.37.06.678.158.926.29.314.164.534.4.66.706.133.301.199.807.199 1.517v1.932c0 .7.14 1.198.421 1.494.281.291.755.437 1.421.437.033 0 .088-.003.166-.008a2.88 2.88 0 01.173-.007v1.601h-.306c-.688 0-1.173.143-1.454.43-.28.286-.421.781-.421 1.486v1.932c0 .73-.066 1.244-.198 1.54a1.381 1.381 0 01-.661.69 3.386 3.386 0 01-.942.283 8.634 8.634 0 01-1.686.085 9.241 9.241 0 00-.322-.008v-1.602c.05 0 .118.003.207.008.088.005.148.008.181.008.557 0 .931-.1 1.124-.3.198-.193.297-.602.297-1.226v-1.831c0-.767.124-1.306.372-1.617.248-.317.694-.542 1.339-.675-.645-.138-1.09-.365-1.339-.682-.248-.317-.372-.853-.372-1.61v-1.854c0-.623-.099-1.032-.297-1.226-.193-.2-.567-.299-1.124-.299-.033 0-.093.003-.181.008a3.778 3.778 0 01-.207.007v-1.601"/><path id="text5554" fill="#C9DCEA" fill-rule="nonzero" d="M390.43 22.88V37.39h-3.994v-1.62h1.862V24.5h-1.862v-1.618h3.993"/><path id="text5641" fill="#181717" fill-rule="nonzero" d="M79.428 86.304l-1.594 4.852h3.196l-1.602-4.852m-1.376-2.283h2.736l4.15 11.399h-2.485l-.782-2.345h-4.463l-.758 2.345h-2.493l4.095-11.4"/><path id="text5645" fill="#181717" fill-rule="nonzero" d="M114.889 92.487h2.242c.085.645.333 1.126.743 1.44.41.31 1.002.464 1.777.464.66 0 1.158-.125 1.493-.376.335-.25.502-.621.502-1.112 0-.715-.962-1.307-2.887-1.776a6.848 6.848 0 01-.3-.072c-1.03-.24-1.765-.51-2.205-.809a2.56 2.56 0 01-.893-1.072c-.205-.453-.307-.987-.307-1.6 0-1.147.365-2.025 1.095-2.633.73-.614 1.785-.92 3.165-.92 1.29 0 2.297.325 3.022.976.73.65 1.115 1.568 1.155 2.753h-2.182c-.04-.571-.245-1.006-.615-1.305-.37-.298-.898-.448-1.583-.448-.595 0-1.055.126-1.38.376-.32.246-.48.598-.48 1.057 0 .624.628 1.09 1.883 1.4l.795.2c.805.219 1.375.39 1.71.512.34.123.635.259.885.408.45.267.787.622 1.012 1.065.225.437.338.962.338 1.576 0 1.227-.388 2.18-1.163 2.857-.775.672-1.867 1.008-3.277 1.008-1.39 0-2.48-.344-3.27-1.032-.79-.688-1.215-1.667-1.275-2.937"/><path id="text5649" fill="#181717" fill-rule="nonzero" d="M156.959 93.361h1.748c1.012 0 1.743-.284 2.194-.851.455-.573.683-1.499.683-2.778 0-1.274-.21-2.21-.633-2.81-.422-.598-1.081-.897-1.978-.897h-2.014v7.336m-2.137 2.059v-11.4h4.15c1.627 0 2.838.473 3.634 1.417.8.944 1.201 2.376 1.201 4.295 0 1.042-.149 1.96-.446 2.755-.293.794-.72 1.434-1.28 1.919a3.9 3.9 0 01-1.44.782c-.536.154-1.29.232-2.258.232h-3.561"/><path id="text5653" fill="#181717" fill-rule="nonzero" d="M194.755 95.42v-11.4h7.987v1.982h-5.664v2.584h4.965v1.982h-4.965v4.852h-2.323"/><path id="text5657" fill="#181717" fill-rule="nonzero" d="M241.917 94.733c-.4.593-.856 1.029-1.368 1.306-.508.278-1.106.417-1.794.417-1.507 0-2.73-.572-3.667-1.715-.931-1.148-1.397-2.654-1.397-4.519 0-1.88.466-3.384 1.397-4.51.932-1.128 2.174-1.691 3.726-1.691 1.352 0 2.454.355 3.308 1.065.854.705 1.362 1.678 1.523 2.917h-2.262c-.137-.614-.417-1.082-.842-1.402-.42-.32-.968-.481-1.647-.481-.902 0-1.612.363-2.13 1.09-.512.72-.768 1.72-.768 2.996 0 1.282.268 2.286.805 3.013.537.726 1.274 1.09 2.21 1.09.703 0 1.291-.227 1.764-.681.474-.454.767-1.077.879-1.867h-2.445v-2.02h4.465v6.386h-1.486l-.27-1.394"/><path id="text5661" fill="#181717" fill-rule="nonzero" d="M274.622 95.42v-11.4h2.25v4.25h4.485v-4.25h2.25v11.4h-2.25v-5.054h-4.485v5.054h-2.25"/><path id="text5665" fill="#181717" fill-rule="nonzero" d="M314.556 90.978h2.484v1.34c0 .386.13.682.39.888.26.2.633.301 1.12.301.53 0 .9-.123 1.11-.369.209-.246.313-.707.313-1.385v-7.732h2.57v7.837c0 .703-.049 1.22-.145 1.551a2.23 2.23 0 01-.458.866c-.327.372-.777.655-1.348.851-.57.196-1.24.294-2.009.294-.707 0-1.331-.086-1.874-.256-.542-.171-.995-.424-1.356-.76a2.392 2.392 0 01-.62-.904c-.118-.337-.177-.889-.177-1.657v-.865"/><path id="text5669" fill="#181717" fill-rule="nonzero" d="M353.49 95.42v-11.4h2.31v4.683l4.382-4.682h2.888l-4.48 4.612 4.884 6.787h-2.82l-3.658-5.216-1.196 1.207v4.009h-2.31"/><path id="text5673" fill="#181717" fill-rule="nonzero" d="M394.423 95.42v-11.4h2.368v9.295h5.618v2.105h-7.986"/><path id="text5679" fill="#181717" fill-rule="nonzero" d="M264.639 55.005v-1.98l6.156-7.353h-6.033v-2.066h8.862v1.981l-6.172 7.36h6.034v2.058h-8.847"/><path id="text5683" fill="#181717" fill-rule="nonzero" d="M134.855 134.798l3.55-5.82-3.55-5.579h2.684l2.308 3.985 2.293-3.985h2.699l-3.55 5.564 3.55 5.835h-2.684l-2.308-3.947-2.308 3.947h-2.684"/><path id="text5687" fill="#181717" fill-rule="nonzero" d="M184.772 131.451c-.084 1.341-.564 2.407-1.441 3.197-.872.79-2.01 1.186-3.417 1.186-1.62 0-2.88-.545-3.78-1.634-.897-1.09-1.345-2.618-1.345-4.583 0-2.009.458-3.547 1.374-4.616.916-1.068 2.233-1.602 3.952-1.602 1.396 0 2.498.369 3.305 1.106.812.731 1.258 1.768 1.337 3.108h-2.22c-.095-.667-.347-1.175-.758-1.522-.412-.352-.966-.529-1.664-.529-.99 0-1.743.345-2.259 1.034-.515.689-.772 1.696-.772 3.02 0 1.283.255 2.273.765 2.973.515.7 1.246 1.05 2.191 1.05.684 0 1.243-.187 1.68-.561.435-.38.717-.921.846-1.627h2.206"/><path id="text5691" fill="#181717" fill-rule="nonzero" d="M217.625 134.798l-3.901-11.399h2.58l2.404 8.528 2.45-8.528h2.55l-3.856 11.399h-2.227"/><path id="text5695" fill="#181717" fill-rule="nonzero" d="M256.813 132.74h2.67c.63 0 1.088-.12 1.376-.357.287-.237.431-.61.431-1.122 0-.531-.141-.913-.424-1.145-.282-.237-.749-.356-1.398-.356h-2.655v2.98m0-4.93h2.573c.555 0 .962-.098 1.22-.294.258-.201.387-.516.387-.944 0-.418-.127-.72-.38-.906-.248-.19-.662-.286-1.242-.286h-2.558v2.43m-2.157 6.988v-11.399h5.161c1.121 0 1.974.248 2.56.743.59.49.884 1.205.884 2.143 0 .578-.109 1.06-.327 1.448a2.183 2.183 0 01-.982.897c.565.238.987.58 1.265 1.03.282.443.424.998.424 1.663 0 1.11-.348 1.966-1.042 2.57-.694.603-1.678.905-2.953.905h-4.99"/><path id="text5699" fill="#181717" fill-rule="nonzero" d="M294.589 134.798v-11.399h2.346l4.426 7.831V123.4h2.213v11.399h-2.317l-4.455-7.832v7.832h-2.213"/><path id="text5703" fill="#181717" fill-rule="nonzero" d="M333.524 134.798v-11.399h3.324l2.174 8.675 2.145-8.675h3.339v11.399h-2.107v-9.194l-2.22 9.194h-2.3l-2.248-9.194v9.194h-2.107"/><g id="g3476" transform="translate(561.145 70.55)"><path id="path5745" fill="#181717" d="M10.246 14.192V4.731L0 9.46z"/><path id="path5747" stroke="#181717" stroke-width="3" d="M25.615 0v9.462H5.123"/></g><path id="path5757" stroke="#181717" stroke-width="2" d="M18.55 130.653l-7.488 9.672h4.991v4.836h4.992v-4.836h4.992z"/><path id="path5801" stroke="#181717" stroke-width="2" d="M501.744 130.653l-7.488 9.672h4.992v4.836h4.992v-4.836h4.991z"/><path id="path3083" fill="#181717" fill-rule="nonzero" d="M447.271 12.398c0-.055-.002-.128-.008-.219a4.189 4.189 0 01-.008-.21c0-.443.051-.81.153-1.102.101-.297.264-.574.488-.83.171-.191.425-.41.761-.656.342-.247.564-.425.665-.536.214-.226.36-.43.44-.611.08-.181.12-.38.12-.596 0-.478-.14-.85-.424-1.117-.283-.266-.678-.4-1.185-.4-.508 0-.908.161-1.202.483-.288.317-.446.765-.473 1.343h-2.259v-.234c0-1.06.356-1.906 1.066-2.534.715-.634 1.677-.951 2.884-.951 1.233 0 2.213.3 2.94.898.731.593 1.097 1.39 1.097 2.391 0 .352-.043.67-.128.95-.08.277-.206.531-.377.763-.219.291-.558.608-1.017.95-.454.337-.729.546-.825.626-.203.186-.35.383-.44.589a1.927 1.927 0 00-.12.867c.005.06.007.106.007.136h-2.155m-.112 3.229v-2.21h2.363v2.21h-2.363"/><path id="text5819" fill="#181717" fill-rule="nonzero" d="M286.602 17.7l3.912-13.472h1.08l-3.927 13.471h-1.065"/><path id="text5928" fill="#C9DCEA" fill-rule="nonzero" d="M173.79 71.348c-.084 1.34-.564 2.406-1.44 3.197-.872.79-2.011 1.186-3.418 1.186-1.619 0-2.88-.545-3.78-1.635-.897-1.09-1.345-2.617-1.345-4.583 0-2.008.458-3.547 1.374-4.615.916-1.068 2.234-1.603 3.952-1.603 1.396 0 2.498.369 3.305 1.106.813.732 1.258 1.768 1.337 3.109h-2.22c-.095-.668-.347-1.175-.758-1.522-.411-.353-.966-.53-1.664-.53-.99 0-1.743.345-2.258 1.034-.515.69-.773 1.696-.773 3.021 0 1.282.255 2.273.765 2.973.515.7 1.246 1.05 2.192 1.05.683 0 1.243-.188 1.678-.562.436-.379.718-.921.847-1.626h2.206"/><path id="text5956" fill="#C9DCEA" fill-rule="nonzero" d="M161.288 70.602h7.481l-.475.983h-7.482l.476-.983m1.027-2.125h7.482l-.476.983h-7.481l.475-.983"/><path id="path3060" fill="#181717" fill-rule="nonzero" d="M246.307 31.467c0 .634.177 1.136.53 1.507.36.371.846.557 1.46.557.573 0 1.022-.18 1.347-.541.324-.36.486-.863.486-1.507 0-.593-.168-1.054-.504-1.383-.336-.33-.808-.495-1.416-.495-.597 0-1.063.165-1.4.495-.335.33-.503.785-.503 1.367m-.035-2.89c.284-.257.626-.45 1.025-.58.4-.133.846-.2 1.339-.2 1.216 0 2.19.33 2.92.989.735.66 1.103 1.538 1.103 2.635 0 1.164-.4 2.105-1.2 2.82-.799.717-1.853 1.075-3.162 1.075-1.512 0-2.662-.464-3.45-1.391-.782-.927-1.173-2.285-1.173-4.073 0-1.921.42-3.39 1.26-4.405.84-1.02 2.05-1.53 3.632-1.53 1.1 0 1.987.255 2.66.765.677.51 1.048 1.2 1.111 2.071h-2.52c-.058-.355-.214-.623-.469-.803-.255-.18-.611-.27-1.069-.27-.625 0-1.106.241-1.442.726-.336.479-.524 1.203-.565 2.171"/><path id="rect10188" fill="#FFF" stroke="#181717" d="M508.233 79.876h39.933v39.378h-39.933z"/><path id="path2917" fill="#C9DCEA" fill-rule="nonzero" d="M532.193 69.513v1.784c-.512.383-.997.665-1.455.848a3.66 3.66 0 01-1.344.267c-.226 0-.452-.022-.679-.065a4.552 4.552 0 01-.68-.186 22.53 22.53 0 01-.524-.201c-1.078-.404-1.897-.606-2.458-.606-.419 0-.857.102-1.315.307-.453.199-.962.52-1.528.96v-1.784c.526-.387 1.029-.675 1.506-.864.478-.194.928-.29 1.351-.29.576 0 1.293.156 2.15.468a.111.111 0 00.029.008c.088.032.224.083.406.153.763.296 1.354.444 1.772.444.418 0 .85-.1 1.292-.298.443-.2.935-.514 1.477-.945"/><path id="path2915" fill="#181717" fill-rule="nonzero" d="M517.137 108.474l-.651 1.887h1.856l.636-1.887h-1.841m0-4.764h1.634l-1.08 3.21h1.79l1.094-3.21h1.635l-1.095 3.21h2.093l-.547 1.554h-2.07l-.622 1.871h2.145l-.526 1.547h-2.152l-1.094 3.217h-1.634l1.094-3.217h-1.804l-1.102 3.217h-1.627l1.08-3.217h-2.123l.562-1.547h2.086l.636-1.871h-2.211l.547-1.555h2.196l1.095-3.21"/><path id="rect10196" fill="#FFF" stroke="#181717" d="M49.997 119.254H89.93v39.378H49.997z"/><path id="text5910" fill="#C9DCEA" fill-rule="nonzero" d="M106.902 31.171c.018-.655.181-1.209.49-1.661.316-.452.87-.91 1.661-1.373l.473-.262c1.101-.594 1.652-1.167 1.652-1.72 0-.323-.11-.577-.33-.763-.22-.187-.524-.28-.911-.28-.428 0-.759.116-.99.347-.233.226-.349.551-.349.975v.068h-1.553c0-.83.259-1.47.776-1.915.518-.447 1.259-.67 2.223-.67.869 0 1.56.203 2.071.61.518.401.777.938.777 1.61 0 .486-.134.904-.402 1.255-.268.35-.791.754-1.571 1.211-.173.108-.417.249-.732.424-.714.395-1.116.706-1.205.932h3.838v1.212h-5.918"/><path id="text5922" fill="#C9DCEA" fill-rule="nonzero" d="M146.835 29.844h1.603c.012.4.127.7.344.898.217.197.543.296.978.296.417 0 .74-.093.969-.28.229-.187.343-.453.343-.799 0-.35-.132-.614-.396-.79-.264-.181-.66-.272-1.19-.272h-.255v-.98h.185c.488 0 .846-.076 1.075-.23.235-.16.352-.4.352-.725 0-.274-.1-.49-.3-.65-.199-.165-.466-.247-.8-.247-.37 0-.656.093-.855.28-.2.181-.3.442-.3.782v.05h-1.568c.018-.72.265-1.269.74-1.647.482-.385 1.166-.577 2.053-.577.834 0 1.491.17 1.973.51.487.341.731.805.731 1.392 0 .308-.085.58-.255.815-.165.231-.412.42-.74.569.428.159.751.381.969.667.223.28.334.609.334.988 0 .713-.276 1.279-.828 1.696-.546.411-1.292.617-2.237.617-.934 0-1.656-.203-2.167-.609-.505-.406-.758-.98-.758-1.72v-.034"/><path id="path3131" fill="#181717" fill-rule="nonzero" d="M131.142 12.276c.232-.12.406-.254.522-.4a.747.747 0 00.174-.472.728.728 0 00-.2-.519c-.133-.147-.409-.317-.826-.51l-2.306-1.078c-.226.121-.4.257-.522.41a.789.789 0 00-.174.486c0 .205.084.386.253.543.174.152.554.364 1.14.636l1.94.904m-4.733 2.57h2.149c.046.41.18.713.4.912.226.2.545.299.957.299.383 0 .687-.081.913-.244a.799.799 0 00.34-.676c0-.23-.085-.43-.253-.597-.168-.173-.501-.37-1-.59l-2.08-.95c-.66-.3-1.154-.642-1.479-1.03a1.98 1.98 0 01-.487-1.305c0-.466.125-.875.374-1.226.256-.356.63-.652 1.123-.888a2.377 2.377 0 01-.644-.77 2.09 2.09 0 01-.209-.936c0-.786.299-1.417.896-1.894.598-.482 1.392-.723 2.384-.723 1.02 0 1.821.236 2.401.707.58.472.879 1.124.896 1.957h-2.053c-.012-.35-.128-.618-.348-.801-.215-.184-.525-.275-.93-.275-.36 0-.639.07-.836.212a.7.7 0 00-.287.597c0 .21.113.406.34.59.231.178.646.393 1.243.644l1.462.613c.742.32 1.29.676 1.644 1.07.354.387.53.835.53 1.343 0 .477-.133.894-.4 1.25-.26.35-.637.623-1.13.817.336.267.588.564.757.888.168.325.252.682.252 1.07 0 .806-.32 1.472-.957 1.996-.638.529-1.459.793-2.462.793-1.102 0-1.95-.24-2.54-.723-.592-.482-.914-1.192-.966-2.13"/><path id="text6073" fill="#181717" fill-rule="nonzero" d="M444.34 35.039v-8.34c0-.83.334-1.5 1.002-2.01.668-.514 1.559-.772 2.672-.772 1.135 0 2.023.25 2.665.75.642.5.963 1.187.963 2.062 0 .54-.095.983-.287 1.328-.186.345-.477.61-.87.795.611.235 1.07.58 1.375 1.035.31.455.466 1.014.466 1.68 0 1.12-.334 2.024-1.002 2.714-.668.69-1.544 1.035-2.626 1.035-.15 0-.319-.007-.505-.022a10.34 10.34 0 01-.59-.068v-1.755a2.769 2.769 0 00.52.03c.611 0 1.083-.162 1.414-.487.331-.33.497-.798.497-1.403 0-.585-.181-1.03-.544-1.334-.357-.305-.88-.458-1.569-.458h-.21l.008-1.537.101.015h.163c.461 0 .81-.11 1.049-.33.243-.225.365-.55.365-.975 0-.425-.127-.748-.38-.968-.25-.225-.614-.337-1.096-.337-.445 0-.785.125-1.018.375-.233.245-.35.605-.35 1.08v7.897h-2.214"/><path id="path2750" fill="#181717" fill-rule="nonzero" d="M417.384 129.151v-2.643h1.997v2.643h-1.997m0 6.683v-2.626h1.997v2.626h-1.997"/><path id="path2746" fill="#181717" fill-rule="nonzero" d="M377.464 128.949v-2.441h1.984v2.44h-1.984m0 8.959v-.943c.37-.129.641-.327.817-.596.176-.268.264-.618.264-1.047v-.202h-1.094v-2.424h1.997v2.328c0 .805-.167 1.45-.501 1.933-.334.489-.828.806-1.483.95"/><path id="text5803" fill="#181717" fill-rule="nonzero" d="M377.45 157.596v-.937c.37-.128.642-.326.818-.593.176-.267.264-.614.264-1.04v-.201h-1.081v-2.41h1.997v2.314c0 .795-.17 1.436-.508 1.922-.334.486-.83.8-1.49.945"/><path id="text5813" fill="#181717" fill-rule="nonzero" d="M417.384 154.487v-2.072h1.997v2.072h-1.997"/><path id="path2737" fill="#181717" fill-rule="nonzero" d="M63.973 145.16v1.75l-6.81 2.918 6.81 2.909v1.75l-8.985-3.876v-1.59l8.985-3.86"/><path id="path2742" fill="#181717" fill-rule="nonzero" d="M54.988 125.472l8.985 3.86v1.59l-8.985 3.876v-1.75l6.827-2.91-6.827-2.916v-1.75"/><path id="text6003" fill="#181717" fill-rule="nonzero" d="M477.764 86.458l-1.594 4.77h3.196l-1.602-4.77m-1.375-2.244h2.735l4.15 11.206h-2.485l-.782-2.305h-4.463l-.758 2.305h-2.493l4.096-11.206m1.868-1.43v-1.872h1.766v1.871h-1.766m-2.72 0v-1.871h1.766v1.871h-1.766"/><path id="text6023" fill="#181717" fill-rule="nonzero" d="M464.306 43.7h2.33v6.881c0 .85.173 1.47.518 1.862.345.388.893.581 1.645.581.761 0 1.314-.193 1.66-.58.35-.388.525-1.009.525-1.863V43.7h2.307v7.127c0 1.346-.386 2.378-1.157 3.098-.767.72-1.873 1.08-3.32 1.08-1.457 0-2.574-.357-3.35-1.072-.772-.72-1.158-1.755-1.158-3.106V43.7m4.972-1.37v-1.833H471v1.833h-1.72m-2.65 0v-1.833h1.72v1.833h-1.72"/><path id="text6041" fill="#181717" fill-rule="nonzero" d="M434.716 89.64c0 1.207.274 2.145.824 2.816.549.665 1.316.998 2.303.998.996 0 1.771-.333 2.326-.998.554-.67.83-1.61.83-2.816 0-1.206-.276-2.142-.83-2.807-.555-.67-1.33-1.006-2.326-1.006-.987 0-1.754.333-2.303.998-.55.666-.824 1.604-.824 2.815m-2.357 0c0-1.772.494-3.177 1.48-4.215.991-1.043 2.326-1.564 4.004-1.564 1.677 0 3.012.521 4.003 1.564.997 1.043 1.495 2.448 1.495 4.215 0 1.768-.498 3.173-1.495 4.216-.991 1.042-2.326 1.564-4.003 1.564-1.678 0-3.013-.522-4.004-1.564-.986-1.043-1.48-2.448-1.48-4.216m5.964-6.896v-1.832h1.724v1.832h-1.724m-2.654 0v-1.832h1.724v1.832h-1.724"/><path id="path2893" fill="#C9DCEA" fill-rule="nonzero" d="M90.991 73.43c-.274.422-.59.733-.949.934-.359.195-.782.293-1.27.293-.788 0-1.388-.215-1.802-.647-.408-.431-.613-1.069-.613-1.912 0-1.199.347-2.205 1.04-3.018.697-.812 1.554-1.219 2.57-1.219.4 0 .745.083 1.04.249.293.165.535.411.724.737l.397-.737h1.33l-1.099 4.5a1.757 1.757 0 00-.052.309c0 .175.06.308.18.399.119.085.296.128.53.128.194 0 .396-.05.605-.151.215-.1.414-.24.598-.421a3.96 3.96 0 00.972-1.408 4.545 4.545 0 00.336-1.768c0-1.205-.463-2.193-1.39-2.966-.922-.777-2.113-1.166-3.573-1.166a7.25 7.25 0 00-1.965.256 5.609 5.609 0 00-1.645.76 5.949 5.949 0 00-1.958 2.145 5.946 5.946 0 00-.68 2.822c0 1.54.516 2.772 1.547 3.695 1.032.918 2.41 1.378 4.133 1.378.808 0 1.595-.123 2.362-.37a7.857 7.857 0 002.153-1.068l.613.88a8.585 8.585 0 01-2.43 1.303 8.199 8.199 0 01-2.668.436c-.956 0-1.836-.123-2.638-.369a6.607 6.607 0 01-2.138-1.076c-.772-.607-1.35-1.307-1.734-2.1-.383-.792-.575-1.69-.575-2.694 0-.838.13-1.636.388-2.393a6.747 6.747 0 011.159-2.077 7.198 7.198 0 012.66-2.062c1.037-.467 2.19-.7 3.461-.7.842 0 1.632.115 2.37.346a6.101 6.101 0 011.935.963c.673.527 1.174 1.129 1.503 1.806.334.673.5 1.433.5 2.28 0 .829-.164 1.594-.493 2.296a4.984 4.984 0 01-1.405 1.791c-.349.281-.73.497-1.144.648a3.781 3.781 0 01-1.278.218c-.528 0-.926-.106-1.195-.316-.265-.211-.402-.522-.412-.933m.374-3.222c-.065-.406-.21-.715-.433-.925-.22-.216-.511-.324-.875-.324-.568 0-1.061.311-1.48.933-.418.622-.628 1.37-.628 2.243 0 .462.1.813.3 1.054.204.24.498.36.881.36.414 0 .79-.157 1.129-.473.344-.316.578-.728.703-1.234l.403-1.634"/><path id="path2898" fill="#C9DCEA" fill-rule="nonzero" d="M469.216 37.389l-3.912-13.472h1.065l3.927 13.472h-1.08"/><path id="path2919" fill="#181717" fill-rule="nonzero" d="M516.22 82.984h1.996v5.182h-1.996v-5.182"/><path id="text6099" fill="#D35155" fill-rule="nonzero" d="M490.263 22.88l-2.747 3.11h-1.246l1.729-3.11h2.264"/><path id="text5103" fill="#D35155" fill-rule="nonzero" d="M485.271 3.192h2.265l1.729 3.109h-1.247l-2.747-3.11"/><path id="text6117" fill="#C9DCEA" fill-rule="nonzero" d="M356.42 146.197h2.083l-.548 3.24c-.072.398-.127.776-.166 1.133a8.3 8.3 0 00-.057.864c0 .462.1.808.302 1.036.202.224.51.335.922.335.48 0 .853-.203 1.117-.61.27-.408.493-1.13.67-2.169l.649-3.83h2.082l-1.39 8.137h-1.931l.13-.872c-.202.387-.428.67-.678.85a1.439 1.439 0 01-.872.267c-.322 0-.608-.074-.857-.223-.25-.144-.476-.37-.678-.678l-.648 3.919h-2.06l1.93-11.4"/><path id="path3396" fill="#C9DCEA" fill-rule="nonzero" d="M78.948 141.016h1.997v15.544h-1.997v-15.544"/><path id="path3403" fill="#181717" fill-rule="nonzero" d="M9.564 5.327c-.4 0-.744.145-1.03.435-.28.29-.42.64-.42 1.048 0 .42.14.776.42 1.065.28.29.624.435 1.03.435.395 0 .733-.148 1.013-.443.28-.296.42-.648.42-1.057 0-.409-.14-.758-.42-1.048a1.358 1.358 0 00-1.013-.435m0-1.099c.692 0 1.28.256 1.763.767a2.54 2.54 0 01.733 1.832 2.53 2.53 0 01-.733 1.824 2.382 2.382 0 01-1.78.758 2.36 2.36 0 01-1.762-.75c-.478-.5-.717-1.11-.717-1.832 0-.721.242-1.335.725-1.84a2.373 2.373 0 011.771-.759"/><path id="text6091" fill="#D35155" fill-rule="nonzero" d="M8.632 23.917h1.865l3.56 5.181h-1.74l-2.746-3.426-2.76 3.426H5.07l3.56-5.18"/><path id="path2585" stroke="#181717" stroke-width="2" d="M18.55 109.927l7.487-9.671h-4.992V95.42h-4.992v4.836h-4.991z"/><text id="Strg" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x="10.982" y="185.293">Strg</tspan></text><text id="tspan3451" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x="553.078" y="185.293">Strg</tspan></text><g id="text3456" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal" transform="translate(116.805 167.948)"><text id="tspan3460"><tspan x="0" y="17.345">Al</tspan> <tspan x="0" y="39.345">t</tspan></text></g><text id="tspan3466" fill="#C9DCEA" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x="405.324" y="185.293">Alt Gr</tspan></text><text id="Win" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x="64.892" y="185.293">Win</tspan></text><text id="Win" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x="464.226" y="185.293">Win</tspan></text><text id="Menu" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="499.171" y="185.293">Menu</tspan></text><circle id="Oval" cx="109.94" cy="138" r="26" stroke="#C06334" stroke-width="5"/></g></g></svg> \ No newline at end of file diff --git a/2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/index.html b/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html similarity index 100% rename from 2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/index.html rename to 2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html diff --git a/2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/script.js b/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/script.js similarity index 100% rename from 2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/script.js rename to 2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/script.js diff --git a/2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/style.css b/2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/style.css similarity index 100% rename from 2-ui/3-event-details/5-keyboard-events/keyboard-dump.view/style.css rename to 2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/style.css diff --git a/2-ui/3-event-details/7-keyboard-events/us-layout.svg b/2-ui/3-event-details/7-keyboard-events/us-layout.svg new file mode 100644 index 0000000000..353f225f1b --- /dev/null +++ b/2-ui/3-event-details/7-keyboard-events/us-layout.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="602" height="202" viewBox="0 0 602 202"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="KB_United_States-NoAltGr" transform="translate(1 1)"><path id="rect2186" fill="#FFF" stroke="#181717" d="M0 0h39.604v40H0z"/><path id="rect2218" fill="#FFF" stroke="#181717" d="M40.594 0h39.604v40H40.594z"/><path id="rect2222" fill="#FFF" stroke="#181717" d="M80.198 0h39.604v40H80.198z"/><path id="rect2228" fill="#FFF" stroke="#181717" d="M119.802 0h39.604v40h-39.604z"/><path id="rect2230" fill="#FFF" stroke="#181717" d="M160.396 0H200v40h-39.604z"/><path id="rect2232" fill="#FFF" stroke="#181717" d="M200 0h39.604v40H200z"/><path id="rect2234" fill="#FFF" stroke="#181717" d="M240.594 0h39.604v40h-39.604z"/><path id="rect2236" fill="#FFF" stroke="#181717" d="M280.198 0h39.604v40h-39.604z"/><path id="rect2238" fill="#FFF" stroke="#181717" d="M319.802 0h39.604v40h-39.604z"/><path id="rect2240" fill="#FFF" stroke="#181717" d="M360.396 0H400v40h-39.604z"/><path id="rect2242" fill="#FFF" stroke="#181717" d="M400 0h39.604v40H400z"/><path id="rect2244" fill="#FFF" stroke="#181717" d="M440.594 0h39.604v40h-39.604z"/><path id="rect2246" fill="#FFF" stroke="#181717" d="M480.198 0h39.604v40h-39.604z"/><path id="rect2248" fill="#D1CFCD" stroke="#181717" d="M519.802 0H600v40h-80.198z"/><path id="rect2250" fill="#D1CFCD" stroke="#181717" d="M0 40h60.396v40H0z"/><path id="rect2252" fill="#FFF" stroke="#181717" d="M60.396 40H100v40H60.396z"/><path id="rect2254" fill="#FFF" stroke="#181717" d="M100 40h39.604v40H100z"/><path id="rect2256" fill="#FFF" stroke="#181717" d="M140.594 40h39.604v40h-39.604z"/><path id="rect2258" fill="#FFF" stroke="#181717" d="M180.198 40h39.604v40h-39.604z"/><path id="rect2262" fill="#FFF" stroke="#181717" d="M219.802 40h39.604v40h-39.604z"/><path id="rect2264" fill="#FFF" stroke="#181717" d="M260.396 40H300v40h-39.604z"/><path id="rect2266" fill="#FFF" stroke="#181717" d="M300 40h39.604v40H300z"/><path id="rect2270" fill="#FFF" stroke="#181717" d="M340.594 40h39.604v40h-39.604z"/><path id="rect2272" fill="#FFF" stroke="#181717" d="M380.198 40h39.604v40h-39.604z"/><path id="rect2274" fill="#FFF" stroke="#181717" d="M419.802 40h39.604v40h-39.604z"/><path id="rect2278" fill="#FFF" stroke="#181717" d="M460.396 40H500v40h-39.604z"/><path id="rect2280" fill="#FFF" stroke="#181717" d="M500 40h39.604v40H500z"/><path id="rect2284" fill="#FFF" stroke="#181717" d="M539.604 40H600v40h-60.396z"/><path id="rect2286" fill="#D1CFCD" stroke="#181717" d="M0 80h70.297v40H0z"/><path id="rect2292" fill="#FFF" stroke="#181717" d="M70.297 80h39.604v40H70.297z"/><path id="rect2296" fill="#FFF" stroke="#181717" d="M109.901 80h39.604v40h-39.604z"/><path id="rect2298" fill="#FFF" stroke="#181717" d="M150.495 80h39.604v40h-39.604z"/><path id="rect2300" fill="#FFF" stroke="#181717" d="M190.099 80h39.604v40h-39.604z"/><path id="rect2302" fill="#FFF" stroke="#181717" d="M230.693 80h39.604v40h-39.604z"/><path id="rect2306" fill="#FFF" stroke="#181717" d="M270.297 80h39.604v40h-39.604z"/><path id="rect2308" fill="#FFF" stroke="#181717" d="M309.901 80h39.604v40h-39.604z"/><path id="rect2312" fill="#FFF" stroke="#181717" d="M350.495 80h39.604v40h-39.604z"/><path id="rect2314" fill="#FFF" stroke="#181717" d="M390.099 80h39.604v40h-39.604z"/><path id="rect2316" fill="#FFF" stroke="#181717" d="M430.693 80h39.604v40h-39.604z"/><path id="rect2318" fill="#FFF" stroke="#181717" d="M470.297 80h39.604v40h-39.604z"/><path id="rect2320" fill="#D1CFCD" stroke="#181717" d="M509.901 80H600v40h-90.099z"/><path id="rect2322" fill="#D1CFCD" stroke="#181717" d="M0 120h90.099v40H0z"/><path id="rect2324" fill="#FFF" stroke="#181717" d="M90.099 120h39.604v40H90.099z"/><path id="rect2326" fill="#FFF" stroke="#181717" d="M130.693 120h39.604v40h-39.604z"/><path id="rect2330" fill="#FFF" stroke="#181717" d="M170.297 120h39.604v40h-39.604z"/><path id="rect2334" fill="#FFF" stroke="#181717" d="M209.901 120h39.604v40h-39.604z"/><path id="rect2336" fill="#FFF" stroke="#181717" d="M250.495 120h39.604v40h-39.604z"/><path id="rect2338" fill="#FFF" stroke="#181717" d="M290.099 120h39.604v40h-39.604z"/><path id="rect2340" fill="#FFF" stroke="#181717" d="M330.693 120h39.604v40h-39.604z"/><path id="rect2342" fill="#FFF" stroke="#181717" d="M370.297 120h39.604v40h-39.604z"/><path id="rect2344" fill="#FFF" stroke="#181717" d="M409.901 120h39.604v40h-39.604z"/><path id="rect2346" fill="#FFF" stroke="#181717" d="M450.495 120h39.604v40h-39.604z"/><path id="rect2348" fill="#D1CFCD" stroke="#181717" d="M490.099 120H600v40H490.099z"/><path id="rect2350" fill="#D1CFCD" stroke="#181717" d="M0 160h60.396v40H0z"/><path id="rect2352" fill="#D1CFCD" stroke="#181717" d="M539.604 160H600v40h-60.396z"/><path id="rect2354" fill="#D1CFCD" stroke="#181717" d="M100 160h60.396v40H100z"/><path id="rect2360" fill="#D1CFCD" stroke="#181717" d="M400 160h60.396v40H400z"/><path id="rect2362" fill="#FFF" stroke="#181717" d="M160.396 160H400v40H160.396z"/><path id="rect2364" fill="#F6F3F1" stroke="#181717" d="M60.396 160H100v40H60.396z"/><path id="rect2366" fill="#F6F3F1" stroke="#181717" d="M460.396 160H500v40h-39.604z"/><path id="rect2368" fill="#F6F3F1" stroke="#181717" d="M500 160h39.604v40H500z"/><path id="text5091" fill="#181717" fill-rule="nonzero" d="M50.238 12.434l-.619-5.26V4h2.444v3.174l-.62 5.26h-1.205m-.587 3.254v-2.341h2.356v2.34h-2.356M50.238 36v-7.768h-2.713v-1.595h.174c.99 0 1.709-.153 2.158-.46.455-.307.722-.82.801-1.54h1.817V36h-2.237"/><path id="text5103" fill="#181717" fill-rule="nonzero" d="M15.842 9v1.75c-.559.376-1.088.653-1.587.832a4.387 4.387 0 01-1.466.262c-.248 0-.495-.021-.742-.063a5.413 5.413 0 01-.74-.183c-.13-.042-.32-.108-.573-.198-1.176-.396-2.07-.594-2.682-.594-.457 0-.935.1-1.434.301-.494.196-1.05.51-1.668.943V10.3a7.651 7.651 0 011.644-.848 4.307 4.307 0 011.474-.286c.628 0 1.41.154 2.344.46a.133.133 0 00.032.008c.097.031.245.082.443.15.833.29 1.477.436 1.934.436.456 0 .926-.098 1.41-.293.483-.196 1.02-.505 1.61-.927M8.037 23.069h2.078L11.701 26h-1.144l-2.521-2.931"/><path id="text5127" fill="#181717" fill-rule="nonzero" d="M90.732 13.574c-.291.443-.628.77-1.009.982-.381.206-.831.309-1.35.309-.837 0-1.475-.227-1.914-.681-.434-.454-.651-1.125-.651-2.012 0-1.261.368-2.32 1.104-3.175.74-.855 1.652-1.283 2.732-1.283.423 0 .791.087 1.104.261.312.174.569.433.77.776l.42-.776h1.415l-1.168 4.736a1.833 1.833 0 00-.055.324c0 .185.063.325.19.42.127.09.315.135.564.135.206 0 .42-.053.643-.159.228-.105.44-.253.636-.443.455-.422.8-.916 1.032-1.48a4.741 4.741 0 00.358-1.862c0-1.267-.493-2.307-1.478-3.12-.98-.818-2.245-1.227-3.796-1.227-.747 0-1.443.09-2.089.269-.64.18-1.223.446-1.747.8-.9.602-1.594 1.354-2.08 2.257a6.207 6.207 0 00-.723 2.97c0 1.62.548 2.916 1.644 3.887 1.096.966 2.56 1.45 4.391 1.45a8.25 8.25 0 002.51-.389 8.382 8.382 0 002.287-1.124l.652.926a9.152 9.152 0 01-2.582 1.37 8.79 8.79 0 01-2.835.46c-1.016 0-1.95-.13-2.803-.388a7.047 7.047 0 01-2.272-1.133c-.82-.638-1.435-1.375-1.842-2.21-.408-.833-.612-1.778-.612-2.834 0-.882.138-1.721.413-2.518a7.08 7.08 0 011.231-2.186 7.64 7.64 0 012.828-2.17c1.1-.49 2.327-.736 3.677-.736.894 0 1.734.121 2.517.364.79.238 1.475.576 2.057 1.014.715.554 1.247 1.188 1.597 1.9.354.708.532 1.508.532 2.4 0 .87-.175 1.676-.524 2.415a5.252 5.252 0 01-1.494 1.885 4.054 4.054 0 01-2.573.91c-.561 0-.985-.11-1.27-.332-.281-.222-.427-.55-.437-.982m.397-3.39c-.069-.427-.223-.752-.46-.973-.234-.227-.544-.34-.93-.34-.604 0-1.128.326-1.573.981-.444.655-.667 1.441-.667 2.36 0 .486.106.855.318 1.108.217.254.53.38.937.38.44 0 .84-.166 1.2-.498.365-.333.614-.766.746-1.299l.429-1.718M85.37 36c.01-1.014.228-1.871.651-2.574.424-.707 1.176-1.422 2.256-2.146.164-.11.402-.264.714-.459 1.43-.913 2.145-1.792 2.145-2.637 0-.501-.151-.897-.453-1.188-.302-.29-.715-.435-1.239-.435-.572 0-1.014.18-1.326.538-.307.354-.46.86-.46 1.52v.096H85.56c0-1.299.35-2.3 1.049-3.002s1.694-1.053 2.986-1.053c1.17 0 2.105.32 2.804.958.698.634 1.048 1.479 1.048 2.534 0 .755-.183 1.41-.548 1.964-.365.555-1.072 1.185-2.12 1.893-.223.153-.53.354-.922.602-.995.639-1.557 1.124-1.684 1.457h5.163V36H85.37"/><path id="text5139" fill="#181717" fill-rule="nonzero" d="M130.065 8.85l-.704 1.92h2.008l.688-1.92h-1.992m0-4.85h1.768l-1.168 3.267h1.936L133.785 4h1.769l-1.185 3.267h2.265l-.592 1.582h-2.24l-.673 1.905h2.32l-.568 1.575h-2.328l-1.184 3.274h-1.768l1.184-3.274h-1.952l-1.192 3.274h-1.76l1.168-3.274h-2.297l.609-1.575h2.256l.688-1.905h-2.392l.592-1.582h2.376L130.065 4m-3.552 28.245h2.2c.005.63.16 1.105.464 1.425.304.315.755.472 1.352.472.576 0 1.019-.147 1.328-.44.315-.3.472-.727.472-1.284 0-.556-.184-.976-.552-1.26-.363-.288-.904-.432-1.624-.432-.037 0-.096.002-.176.008-.075.005-.13.007-.168.007v-1.558h.248c.661 0 1.15-.126 1.464-.378.32-.257.48-.648.48-1.173 0-.436-.136-.78-.408-1.031-.267-.257-.632-.386-1.096-.386-.507 0-.899.147-1.176.44-.277.295-.416.712-.416 1.253v.094h-2.16c.026-1.155.37-2.039 1.032-2.653.667-.62 1.605-.929 2.816-.929 1.147 0 2.056.276 2.728.827.672.55 1.008 1.293 1.008 2.228 0 .493-.114.926-.344 1.298-.229.373-.573.68-1.032.921.587.252 1.03.601 1.328 1.047.304.441.456.969.456 1.583 0 1.128-.376 2.023-1.128 2.684-.752.661-1.778.992-3.08.992-1.285 0-2.278-.32-2.976-.96-.694-.646-1.04-1.562-1.04-2.748v-.047"/><path id="text5145" fill="#181717" fill-rule="nonzero" d="M169.832 14.64c.412-.056.727-.21.944-.458.216-.25.324-.581.324-.996 0-.368-.103-.671-.31-.91-.2-.244-.52-.433-.958-.568v2.933m-.899-5.32V6.675c-.392.052-.692.192-.898.42-.207.223-.31.524-.31.902 0 .316.096.58.287.793.196.213.503.39.921.53m0 8.517v-1.494c-1.137-.072-1.988-.412-2.552-1.019-.564-.606-.846-1.483-.846-2.629h2.024c.025.56.15 1.004.377 1.33.232.322.564.527.997.615v-3.22c-1.168-.306-1.998-.706-2.492-1.198-.493-.493-.74-1.167-.74-2.023 0-.949.292-1.714.876-2.294.59-.581 1.375-.884 2.356-.91V4h.899v.996c.981.051 1.746.357 2.295.917.549.555.85 1.338.906 2.35h-2.054c-.035-.483-.148-.85-.34-1.105-.186-.26-.455-.404-.807-.436v2.832c1.243.399 2.119.855 2.627 1.369.509.513.763 1.19.763 2.03 0 .99-.297 1.781-.891 2.372-.589.591-1.422.936-2.5 1.035v1.478h-.898m1.02 14.04v-4.769l-2.938 4.768h2.938M169.907 36v-2.403h-4.56v-1.96l3.986-6.457h2.68v6.643h1.254v1.774h-1.253V36h-2.107"/><path id="text5151" fill="#181717" fill-rule="nonzero" d="M213.66 12.728c0 .354.119.653.356.897.237.238.529.357.876.357.337 0 .627-.122.869-.365a1.23 1.23 0 00.363-.889c0-.339-.121-.627-.363-.865a1.166 1.166 0 00-.869-.365c-.347 0-.64.12-.876.357a1.173 1.173 0 00-.356.873m-1.713 0c0-.825.284-1.52.853-2.087.568-.566 1.266-.849 2.092-.849.81 0 1.5.289 2.07.865.573.571.86 1.262.86 2.071 0 .815-.287 1.513-.86 2.095a2.814 2.814 0 01-2.07.865c-.826 0-1.524-.286-2.092-.857-.569-.572-.853-1.273-.853-2.103m-4.588 2.96L214.103 4h1.27l-6.743 11.688h-1.271m-.695-8.728c0 .349.116.642.347.88.237.238.53.357.877.357.342 0 .632-.119.869-.357.242-.243.363-.537.363-.88 0-.339-.121-.627-.363-.865a1.166 1.166 0 00-.869-.365c-.347 0-.64.119-.877.357a1.187 1.187 0 00-.347.873m-1.714 0c0-.826.285-1.524.853-2.095.569-.577 1.264-.865 2.085-.865.81 0 1.5.29 2.069.873a2.85 2.85 0 01.86 2.087 2.85 2.85 0 01-.86 2.086 2.81 2.81 0 01-2.069.857c-.827 0-1.524-.283-2.093-.849-.563-.566-.845-1.264-.845-2.094m2.259 25.66h2.21c.038.497.204.883.498 1.158.295.27.69.405 1.185.405.61 0 1.076-.18 1.398-.54.326-.36.49-.875.49-1.547 0-.63-.167-1.124-.498-1.484-.332-.365-.79-.547-1.374-.547-.327 0-.614.066-.861.198a1.757 1.757 0 00-.632.587l-2.037-.119.734-6.093h6.5v1.92h-4.905l-.292 2.277c.248-.196.532-.341.853-.436.321-.1.68-.151 1.074-.151 1.127 0 2.035.344 2.724 1.031.695.688 1.043 1.59 1.043 2.706 0 1.222-.382 2.198-1.145 2.928-.764.725-1.788 1.087-3.072 1.087-1.169 0-2.1-.299-2.795-.897-.69-.603-1.056-1.43-1.098-2.483"/><path id="text5157" fill="#181717" fill-rule="nonzero" d="M248.477 4h2.056l3.922 4.418h-1.916l-3.026-2.922-3.042 2.922h-1.917L248.477 4m-.685 28.078c0 .646.166 1.158.498 1.536.337.378.794.567 1.37.567.538 0 .96-.184 1.264-.552.305-.367.457-.88.457-1.535 0-.604-.158-1.074-.473-1.41-.316-.336-.759-.504-1.33-.504-.56 0-.997.168-1.313.504-.315.336-.473.8-.473 1.394m-.032-2.946a2.55 2.55 0 01.962-.59 3.652 3.652 0 011.256-.205c1.142 0 2.055.336 2.74 1.008.69.672 1.036 1.567 1.036 2.686 0 1.186-.375 2.144-1.125 2.874S250.889 36 249.659 36c-1.418 0-2.497-.473-3.237-1.418-.734-.945-1.1-2.328-1.1-4.15 0-1.959.393-3.455 1.182-4.49.788-1.04 1.924-1.559 3.409-1.559 1.033 0 1.865.26 2.495.78.637.52.984 1.223 1.044 2.11h-2.365c-.054-.362-.201-.635-.44-.819-.24-.183-.574-.275-1.003-.275-.588 0-1.039.246-1.354.74-.316.488-.492 1.226-.53 2.213"/><path id="text5163" fill="#181717" fill-rule="nonzero" d="M288.584 10.586l-.571.436c-.23.17-.404.373-.519.611a1.815 1.815 0 00-.173.801c0 .45.146.828.436 1.135.29.307.654.46 1.09.46.321 0 .627-.069.917-.206.296-.143.57-.35.82-.619l-2-2.618m.602-2.5l.263-.198c.24-.175.418-.37.533-.587.116-.217.173-.466.173-.746 0-.27-.08-.484-.24-.643-.156-.158-.369-.238-.64-.238-.275 0-.493.082-.653.246-.16.159-.24.376-.24.65 0 .149.037.31.112.485.08.174.193.36.338.555l.354.476m-1.692 1.056l-.436-.603a3.832 3.832 0 01-.489-.89 2.72 2.72 0 01-.142-.88c0-.846.255-1.518.766-2.015.517-.503 1.216-.754 2.098-.754.847 0 1.516.23 2.007.69.491.46.737 1.082.737 1.865 0 .592-.145 1.124-.436 1.595-.286.465-.737.904-1.353 1.317l1.586 2.063c.15-.26.268-.55.353-.873a5.97 5.97 0 00.188-1.079h1.955a7.196 7.196 0 01-.406 1.912 6 6 0 01-.887 1.61l2.015 2.588h-2.557l-.751-.976a4.672 4.672 0 01-1.459.976 4.299 4.299 0 01-1.654.325c-1.022 0-1.86-.32-2.51-.96-.647-.64-.97-1.457-.97-2.452 0-.74.18-1.378.54-1.912.362-.534.963-1.05 1.805-1.547M289.742 36h-2.248c.08-1.55.434-3.087 1.06-4.61.632-1.523 1.531-3.02 2.699-4.49h-5.293v-1.937h7.631v1.706c-1.183 1.375-2.092 2.83-2.729 4.364-.631 1.534-1.005 3.19-1.12 4.967"/><path id="text5169" fill="#181717" fill-rule="nonzero" d="M328.705 8.587l-1.353 1.948-1.308-.935 1.477-1.893-2.153-.612.5-1.595 2.068.754V4h1.523v2.254l2.068-.738.516 1.594-2.154.597 1.462 1.878-1.339.982-1.307-1.98m-1.722 23.823c0 .55.148.974.446 1.273.302.298.733.448 1.292.448.533 0 .95-.152 1.253-.456.308-.304.461-.725.461-1.265 0-.518-.156-.934-.469-1.248-.312-.315-.728-.472-1.245-.472-.518 0-.939.16-1.262.48-.317.319-.476.732-.476 1.24m.176-4.822c0 .434.134.77.4 1.005.267.23.649.346 1.146.346.497 0 .88-.118 1.146-.354.272-.235.408-.568.408-.997 0-.42-.139-.75-.416-.99-.277-.24-.656-.361-1.138-.361-.471 0-.848.123-1.13.369-.277.246-.416.573-.416.982m-.792 2.183c-.461-.262-.794-.568-1-.919-.204-.35-.307-.798-.307-1.343 0-.963.323-1.72.969-2.27.646-.55 1.538-.825 2.676-.825 1.149 0 2.046.275 2.692.825.65.545.976 1.301.976 2.27 0 .518-.11.969-.33 1.351-.22.377-.551.68-.992.911.538.257.943.613 1.215 1.068.271.45.407 1 .407 1.65 0 1.094-.353 1.953-1.061 2.576-.702.623-1.671.935-2.907.935-1.25 0-2.222-.309-2.914-.927-.692-.618-1.039-1.48-1.039-2.584 0-.644.129-1.183.385-1.618.261-.44.672-.807 1.23-1.1"/><path id="text5175" fill="#181717" fill-rule="nonzero" d="M372.099 4a14.885 14.885 0 00-1.566 3.611 14.3 14.3 0 00-.512 3.855c0 1.34.17 2.622.512 3.847a14.884 14.884 0 001.566 3.611h-1.612c-.891-1.25-1.568-2.507-2.032-3.768-.458-1.267-.687-2.497-.687-3.69s.229-2.423.687-3.69c.464-1.266 1.14-2.525 2.032-3.776h1.612m-1.169 24.33c0-.644-.158-1.152-.474-1.523-.31-.377-.735-.566-1.275-.566-.51 0-.907.181-1.192.542-.28.361-.42.866-.42 1.515 0 .607.148 1.081.443 1.421.296.34.71.51 1.245.51.525 0 .935-.167 1.23-.502.295-.335.443-.8.443-1.397m.038 2.928a2.45 2.45 0 01-.901.589c-.346.13-.736.196-1.169.196-1.074 0-1.935-.332-2.581-.997-.647-.665-.97-1.554-.97-2.67 0-1.182.35-2.137 1.054-2.865.702-.727 1.63-1.091 2.78-1.091 1.334 0 2.347.474 3.04 1.421.698.942 1.046 2.319 1.046 4.13 0 1.947-.371 3.438-1.115 4.475-.743 1.036-1.813 1.554-3.208 1.554-.962 0-1.739-.256-2.33-.77-.59-.518-.914-1.221-.97-2.111l2.208.008c.05.36.188.63.412.808.224.178.54.267.947.267.55 0 .97-.246 1.26-.738.291-.492.456-1.227.497-2.206"/><path id="text5181" fill="#181717" fill-rule="nonzero" d="M406.175 4h1.63c.888 1.247 1.561 2.504 2.02 3.772.46 1.263.69 2.497.69 3.701 0 1.2-.23 2.434-.69 3.702-.459 1.268-1.132 2.522-2.02 3.764h-1.63a14.864 14.864 0 001.576-3.623c.347-1.23.52-2.512.52-3.843 0-1.335-.173-2.616-.52-3.842A15.002 15.002 0 00406.175 4m1.048 26.224c0 1.367.133 2.36.398 2.978.27.619.7.928 1.286.928.587 0 1.015-.312 1.286-.936.27-.623.405-1.613.405-2.97 0-1.362-.135-2.352-.405-2.97-.27-.619-.7-.928-1.286-.928-.587 0-1.015.31-1.286.927-.265.613-.398 1.604-.398 2.971m-2.273 0c0-1.933.327-3.38.98-4.338.658-.964 1.65-1.446 2.977-1.446 1.321 0 2.311.485 2.97 1.454.663.964.994 2.412.994 4.346 0 1.933-.329 3.376-.987 4.33-.653.953-1.645 1.43-2.977 1.43-1.326 0-2.319-.48-2.977-1.438-.653-.959-.98-2.405-.98-4.338"/><path id="text5187" fill="#181717" fill-rule="nonzero" d="M444.554 18h7.921v1.634h-7.92V18m1.786 15v-2.194h4.347V33h-4.347"/><path id="text5201" fill="#181717" fill-rule="nonzero" d="M488.853 6h1.502v3.952h3.704v1.543h-3.704v3.967h-1.502v-3.967h-3.704V9.952h3.704V6m-3.704 25.458h8.91V33h-8.91v-1.542m0-3.333h8.91v1.543h-8.91v-1.543"/><path id="text5427" fill="#181717" fill-rule="nonzero" d="M7.278 63.794v-6.402H4.95V56h6.253v1.392H8.892v6.402H7.278m8.037-.715c-.195.3-.45.53-.767.688a2.472 2.472 0 01-1.102.233c-.561 0-.995-.159-1.3-.476-.302-.318-.453-.77-.453-1.355 0-.543.151-.957.453-1.243.305-.286.808-.482 1.507-.587.16-.025.37-.053.629-.085.657-.085.985-.273.985-.566 0-.233-.073-.399-.218-.498-.146-.102-.389-.153-.73-.153-.312 0-.552.062-.719.185a.619.619 0 00-.25.53v.084H11.9v-.106c0-.589.21-1.051.629-1.386.419-.339.997-.508 1.736-.508.81 0 1.43.14 1.859.418.433.279.65.68.65 1.206v3.27c0 .24.024.42.074.54a.52.52 0 00.25.264v.26h-1.613a1.337 1.337 0 01-.123-.328 1.884 1.884 0 01-.048-.387m-.026-2.116c-.253.116-.544.21-.874.28-.327.07-.495.108-.506.111-.273.078-.465.175-.575.292-.107.116-.16.276-.16.481 0 .212.07.38.208.508.138.123.325.185.559.185.419 0 .747-.116.985-.35.242-.235.363-.556.363-.962v-.545M18.165 56h1.507v2.757c.192-.3.431-.526.719-.678.291-.155.621-.232.99-.232.7 0 1.272.287 1.716.862.443.571.665 1.316.665 2.233 0 .91-.222 1.645-.665 2.206-.444.561-1.023.841-1.737.841-.373 0-.697-.077-.974-.232-.274-.156-.522-.403-.746-.741v.778h-1.475V56m4.058 4.878c0-.529-.112-.943-.335-1.243a1.082 1.082 0 00-.922-.455c-.412 0-.731.148-.959.444-.223.293-.335.711-.335 1.254 0 .586.106 1.03.32 1.334.216.303.53.455.942.455.42 0 .739-.152.959-.455.22-.307.33-.752.33-1.334"/><path id="text5207" fill="#181717" fill-rule="nonzero" d="M526.261 29.323h1.868c.44 0 .762-.08.963-.241.201-.16.302-.414.302-.76 0-.36-.1-.618-.297-.775-.198-.16-.524-.241-.978-.241h-1.858v2.017m0-3.337h1.8c.39 0 .674-.066.854-.2.18-.135.27-.348.27-.638 0-.283-.088-.487-.265-.613-.173-.13-.463-.194-.869-.194h-1.79v1.645m-1.509 4.73V23h3.611c.784 0 1.381.168 1.79.503.413.332.62.815.62 1.45 0 .392-.077.719-.23.98a1.51 1.51 0 01-.686.608c.395.16.69.393.884.697.198.3.297.676.297 1.126 0 .75-.243 1.33-.729 1.74-.485.408-1.174.612-2.065.612h-3.492m10.625-.707c-.19.297-.44.524-.75.68-.308.155-.667.231-1.076.231-.548 0-.971-.157-1.27-.471-.295-.314-.442-.761-.442-1.341 0-.538.147-.948.442-1.231.299-.283.79-.477 1.473-.582.156-.024.36-.052.614-.083.641-.084.962-.271.962-.56 0-.231-.07-.395-.213-.493-.142-.102-.38-.152-.713-.152-.305 0-.54.06-.702.183-.163.122-.245.297-.245.524v.084h-1.415v-.105c0-.583.205-1.04.614-1.372.41-.336.975-.503 1.696-.503.791 0 1.396.138 1.816.414.423.275.635.674.635 1.194v3.237c0 .238.024.416.073.534.048.116.13.203.244.262v.257h-1.576a1.334 1.334 0 01-.12-.325 1.89 1.89 0 01-.047-.382m-.026-2.095c-.246.115-.53.207-.853.277-.32.07-.484.107-.494.11-.268.077-.455.173-.562.288-.104.116-.156.274-.156.477 0 .21.067.377.202.503.136.122.318.183.547.183.41 0 .73-.115.962-.346.236-.233.354-.551.354-.953v-.54m6.197.655h1.493c-.062.723-.324 1.294-.785 1.713-.462.42-1.055.629-1.78.629-.822 0-1.467-.269-1.935-.807-.465-.541-.698-1.288-.698-2.242 0-.95.238-1.694.713-2.231.479-.542 1.136-.812 1.972-.812.732 0 1.318.195 1.759.586.444.392.692.936.744 1.635h-1.504c-.041-.304-.147-.536-.317-.697-.17-.16-.394-.24-.671-.24-.375 0-.656.148-.843.444-.184.297-.276.742-.276 1.336 0 .548.095.971.286 1.268a.94.94 0 00.833.44c.28 0 .506-.086.676-.257.17-.171.281-.426.333-.765m2.482 2.148V23h1.472v4.05l1.8-2.028h1.822l-1.972 2.111 2.091 3.583h-1.815l-1.353-2.425-.573.623v1.802h-1.472m5.562-1.854h1.509c.014.29.118.506.312.65.194.139.486.209.874.209.295 0 .522-.05.682-.147a.475.475 0 00.244-.424c0-.259-.301-.452-.905-.582a13.134 13.134 0 01-.588-.136c-.735-.188-1.247-.412-1.535-.67-.284-.259-.427-.608-.427-1.048 0-.583.209-1.044.625-1.383.42-.342.987-.513 1.701-.513.756 0 1.35.17 1.78.508.433.339.664.815.692 1.43h-1.473a.706.706 0 00-.276-.56c-.17-.133-.407-.2-.712-.2-.288 0-.503.044-.646.131a.434.434 0 00-.208.393c0 .227.377.428 1.13.603.173.038.31.07.41.094.753.178 1.267.393 1.54.644.278.252.417.606.417 1.064 0 .646-.227 1.138-.682 1.477-.45.339-1.108.508-1.972.508-.78 0-1.39-.176-1.831-.53-.44-.352-.661-.837-.661-1.455v-.063m10.14-.959c0-.573-.107-1.01-.322-1.31-.215-.303-.527-.455-.936-.455-.403 0-.71.15-.921.45-.208.3-.312.739-.312 1.315 0 .538.109.953.327 1.247.222.293.534.44.937.44.381 0 .681-.15.9-.45.219-.301.328-.713.328-1.237M555.768 33v-7.978h1.441v.77c.219-.335.462-.58.729-.733.27-.154.588-.23.952-.23.697 0 1.262.277 1.696.832.434.552.65 1.278.65 2.18 0 .91-.216 1.651-.65 2.22-.434.566-.992.849-1.675.849-.361 0-.684-.077-.968-.23a1.927 1.927 0 01-.702-.671V33h-1.473m9.745-2.991c-.19.297-.44.524-.749.68-.309.155-.668.231-1.077.231-.548 0-.971-.157-1.27-.471-.294-.314-.442-.761-.442-1.341 0-.538.148-.948.443-1.231.298-.283.789-.477 1.472-.582.156-.024.36-.052.614-.083.642-.084.963-.271.963-.56 0-.231-.072-.395-.214-.493-.142-.102-.38-.152-.713-.152-.305 0-.539.06-.702.183-.163.122-.245.297-.245.524v.084h-1.415v-.105c0-.583.205-1.04.614-1.372.41-.336.975-.503 1.696-.503.791 0 1.397.138 1.816.414.423.275.635.674.635 1.194v3.237c0 .238.024.416.073.534.048.116.13.203.244.262v.257h-1.576a1.334 1.334 0 01-.12-.325 1.89 1.89 0 01-.047-.382m-.026-2.095c-.246.115-.53.207-.853.277-.319.07-.484.107-.494.11-.267.077-.455.173-.562.288-.104.116-.156.274-.156.477 0 .21.067.377.203.503.135.122.317.183.546.183.41 0 .73-.115.963-.346.235-.233.353-.551.353-.953v-.54m6.197.655h1.494c-.063.723-.325 1.294-.786 1.713-.461.42-1.055.629-1.78.629-.822 0-1.467-.269-1.935-.807-.465-.541-.697-1.288-.697-2.242 0-.95.237-1.694.712-2.231.48-.542 1.136-.812 1.972-.812.732 0 1.319.195 1.76.586.443.392.691.936.743 1.635h-1.504c-.041-.304-.147-.536-.317-.697-.17-.16-.394-.24-.671-.24-.375 0-.656.148-.843.444-.184.297-.276.742-.276 1.336 0 .548.096.971.286 1.268a.94.94 0 00.833.44c.28 0 .506-.086.676-.257.17-.171.281-.426.333-.765m5.937.42h1.509c-.153.6-.461 1.072-.926 1.414-.462.339-1.025.508-1.691.508-.819 0-1.468-.276-1.946-.828-.479-.555-.718-1.306-.718-2.252 0-.933.236-1.668.707-2.206.472-.537 1.117-.806 1.936-.806.867 0 1.537.265 2.008.796.472.527.708 1.28.708 2.258a4.312 4.312 0 01-.016.398h-3.777c.02.447.128.784.322 1.01.198.228.48.341.849.341.26 0 .473-.05.64-.152.166-.104.298-.265.395-.482m-2.206-1.718h2.227c-.014-.384-.116-.675-.307-.875-.188-.202-.458-.303-.812-.303-.33 0-.59.1-.78.303-.188.203-.297.495-.328.875"/><path id="path5439" stroke="#181717" stroke-width="3" d="M560.396 11.5h-28.713"/><path id="path5441" fill="#181717" d="M534.653 17V7l-9.9 5z"/><g id="g5459" transform="translate(29.703 50)"><path id="path5453" stroke="#181717" stroke-width="3" d="M25.401 5.5H6.611"/><path id="path5455" fill="#181717" d="M10.556 10.656V.344L.66 5.5z"/><path id="path5457" stroke="#181717" stroke-width="3" d="M.66.344v10.312"/></g><g id="g5464" transform="rotate(180 27.723 35.5)"><path id="path5466" stroke="#181717" stroke-width="3" d="M25.401 5.5H6.611"/><path id="path5468" fill="#181717" d="M10.556 10.656V.344L.66 5.5z"/><path id="path5470" stroke="#181717" stroke-width="3" d="M.66.344v10.312"/></g><path id="text5474" fill="#181717" fill-rule="nonzero" d="M71.131 54.066l-1.178-1.225 1.254-1.386 1.208 1.257c.161-.322.285-.69.37-1.104.086-.414.129-.865.129-1.354 0-1.305-.275-2.318-.824-3.038-.548-.726-1.316-1.088-2.303-1.088-.977 0-1.737.36-2.281 1.08-.544.72-.816 1.735-.816 3.046 0 1.306.272 2.321.816 3.047.544.72 1.304 1.08 2.28 1.08.253 0 .49-.027.71-.08a2.74 2.74 0 00.635-.235m1.609 1.62a4.191 4.191 0 01-1.322.613 6.03 6.03 0 01-1.631.21c-1.662 0-2.983-.565-3.965-1.693-.977-1.128-1.466-2.649-1.466-4.562 0-1.918.489-3.439 1.466-4.562.982-1.128 2.303-1.692 3.965-1.692 1.661 0 2.983.564 3.965 1.692.987 1.129 1.48 2.65 1.48 4.562a8.3 8.3 0 01-.302 2.305 5.583 5.583 0 01-.89 1.822l1.208 1.233L74.008 57l-1.268-1.314"/><path id="text5482" fill="#181717" fill-rule="nonzero" d="M106.308 56l-3.338-12h2.483l1.993 8.464L109.131 44h2.554l1.684 8.464L115.362 44h2.46l-3.33 12h-2.285l-1.803-9.19-1.811 9.19h-2.285"/><path id="text5492" fill="#181717" fill-rule="nonzero" d="M145.545 56V44h8.62v2.086h-6.21v2.558h5.678v2.053h-5.677v3.087h6.5V56h-8.911"/><path id="text5500" fill="#181717" fill-rule="nonzero" d="M187.62 49.246h2.903c.63 0 1.093-.127 1.387-.382.299-.256.448-.658.448-1.206 0-.522-.144-.915-.432-1.181-.288-.272-.718-.408-1.289-.408h-3.017v3.177M185.149 56V44h5.823c1.31 0 2.28.28 2.911.84.63.559.946 1.414.946 2.565 0 .734-.155 1.344-.465 1.833a2.307 2.307 0 01-1.32 1.002c.521.185.89.467 1.108.848.223.38.351.972.384 1.776l.049 1.417v.049c.016.717.17 1.151.465 1.303V56h-2.708a2.362 2.362 0 01-.204-.62 6.444 6.444 0 01-.082-.887l-.032-1.263c-.027-.744-.169-1.246-.424-1.507-.25-.26-.704-.391-1.362-.391h-2.618V56h-2.471"/><path id="text5504" fill="#181717" fill-rule="nonzero" d="M229.06 56v-9.857h-3.317V44h8.91v2.143h-3.294V56h-2.3"/><path id="text5518" fill="#181717" fill-rule="nonzero" d="M271.618 56h-2.268v-4.473L265.347 44h2.776l2.357 5.173L272.658 44h2.59l-3.63 7.527V56"/><path id="text5522" fill="#181717" fill-rule="nonzero" d="M305.94 44h2.311v7.304c0 .902.172 1.56.514 1.977.342.41.886.616 1.631.616.755 0 1.304-.205 1.646-.616.348-.411.521-1.07.521-1.977V44h2.288v7.565c0 1.428-.382 2.525-1.147 3.289-.76.764-1.858 1.146-3.293 1.146-1.445 0-2.552-.38-3.323-1.138-.765-.765-1.147-1.863-1.147-3.297V44"/><path id="text5526" fill="#181717" fill-rule="nonzero" d="M349.505 56V44h1.98v12h-1.98"/><path id="text5530" fill="#181717" fill-rule="nonzero" d="M386.495 50c0 1.253.273 2.227.817 2.923.545.69 1.306 1.036 2.284 1.036.989 0 1.758-.346 2.307-1.036.55-.696.825-1.67.825-2.923 0-1.253-.275-2.224-.825-2.915-.55-.696-1.318-1.044-2.307-1.044-.978 0-1.74.346-2.284 1.036-.544.691-.817 1.665-.817 2.923m-2.337 0c0-1.84.49-3.299 1.468-4.376.983-1.083 2.306-1.624 3.97-1.624 1.664 0 2.988.541 3.971 1.624.988 1.082 1.483 2.541 1.483 4.376 0 1.835-.495 3.294-1.483 4.376-.983 1.083-2.307 1.624-3.97 1.624-1.665 0-2.988-.541-3.971-1.624-.978-1.082-1.468-2.54-1.468-4.376"/><path id="text5534" fill="#181717" fill-rule="nonzero" d="M428.156 49.556h2.319c.628 0 1.084-.136 1.37-.407.284-.277.426-.72.426-1.328 0-.57-.14-1.002-.419-1.296-.28-.293-.696-.44-1.25-.44h-2.446v3.471m-.016 2.175V56h-2.397V44h5.183c1.224 0 2.15.329 2.778.986.633.651.95 1.61.95 2.876 0 1.238-.31 2.194-.926 2.867-.618.668-1.499 1.002-2.644 1.002h-2.944"/><path id="text5546" fill="#181717" fill-rule="nonzero" d="M473.267 43.016v1.634c-.049 0-.117-.002-.205-.008a3.648 3.648 0 00-.18-.007c-.552 0-.926.101-1.122.305-.192.198-.287.62-.287 1.267v1.877c0 .771-.123 1.319-.369 1.642-.246.324-.688.556-1.327.696.639.141 1.081.37 1.327.689.246.318.369.863.369 1.634v1.885c0 .642.095 1.061.287 1.26.19.198.565.297 1.122.297.033 0 .093-.003.18-.008.088-.005.156-.008.205-.008v1.635c-.076 0-.183.002-.32.007a8.326 8.326 0 01-1.672-.086 3.288 3.288 0 01-.933-.289 1.51 1.51 0 01-.664-.72c-.125-.297-.188-.816-.188-1.556v-1.97c0-.715-.14-1.221-.418-1.518-.279-.303-.748-.454-1.41-.454-.032 0-.087.003-.163.008a2.764 2.764 0 01-.172.008V49.6c.038 0 .095.003.172.008.076.005.13.008.164.008.655 0 1.122-.151 1.4-.454.285-.302.427-.813.427-1.533v-1.955c0-.746.063-1.27.188-1.572a1.51 1.51 0 01.664-.72c.257-.13.568-.227.934-.29a8.326 8.326 0 011.672-.085c.136.005.243.008.32.008m-4.302 20.053h4.137v1.666h-1.925v11.6h1.925V78h-4.137V63.07"/><path id="text5554" fill="#181717" fill-rule="nonzero" d="M506.93 43h.558c.6 0 1.084.031 1.45.094.366.063.672.162.918.297.311.167.53.407.655.72.132.308.197.824.197 1.55v1.971c0 .715.14 1.224.418 1.526.279.297.748.446 1.41.446.032 0 .087-.003.163-.008.077-.005.134-.008.172-.008v1.636h-.303c-.683 0-1.163.146-1.442.438-.279.292-.418.798-.418 1.518v1.972c0 .746-.065 1.27-.197 1.573a1.393 1.393 0 01-.655.704c-.257.13-.568.227-.934.29a8.322 8.322 0 01-1.672.085 8.903 8.903 0 00-.32-.007V56.16c.05 0 .118.003.206.008.087.005.147.008.18.008.552 0 .923-.102 1.114-.305.197-.199.295-.616.295-1.252v-1.87c0-.783.123-1.333.369-1.651.246-.324.688-.553 1.327-.689-.639-.14-1.081-.373-1.327-.696-.246-.324-.369-.872-.369-1.644v-1.893c0-.637-.098-1.054-.295-1.252-.191-.204-.562-.305-1.114-.305-.033 0-.093.002-.18.008a3.64 3.64 0 01-.205.007V43m4.375 20.063V78h-4.146v-1.667h1.934V64.73h-1.934v-1.666h4.146"/><path id="text5564" fill="#181717" fill-rule="nonzero" d="M549.198 42h1.611v16.137h-1.61V42m2.205 34l-3.88-13.403h1.057L552.475 76h-1.071"/><g id="g5770" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="13" font-weight="normal" transform="translate(2.97 83)"><g id="text5570" transform="translate(0 .166)"><text id="tspan5572"><tspan x=".102" y="14.34">Caps Lock</tspan></text></g></g><path id="text5641" fill="#181717" fill-rule="nonzero" d="M79.684 86.403l-1.582 5.108h3.17l-1.588-5.108M78.319 84h2.713l4.117 12h-2.466l-.775-2.468h-4.426L76.73 96h-2.473l4.062-12"/><path id="text5645" fill="#181717" fill-rule="nonzero" d="M115.842 92.17h2.224c.084.623.33 1.086.736 1.39.407.298.994.448 1.763.448.654 0 1.148-.121 1.48-.363.332-.242.498-.6.498-1.074 0-.69-.954-1.26-2.863-1.714a6.949 6.949 0 01-.298-.07c-1.021-.231-1.75-.491-2.187-.78a2.488 2.488 0 01-.885-1.034c-.203-.438-.305-.952-.305-1.544 0-1.107.362-1.954 1.086-2.541.724-.592 1.77-.888 3.14-.888 1.278 0 2.278.314 2.997.942.724.628 1.105 1.514 1.145 2.656h-2.164c-.04-.55-.243-.97-.61-1.258-.367-.289-.89-.433-1.57-.433-.59 0-1.046.121-1.368.363-.318.237-.476.577-.476 1.02 0 .602.622 1.052 1.867 1.35l.788.194c.798.211 1.364.376 1.696.494.337.119.63.25.878.394.446.257.78.6 1.004 1.027.223.422.334.93.334 1.521 0 1.184-.384 2.103-1.152 2.757-.769.649-1.853.973-3.25.973-1.38 0-2.46-.332-3.244-.996-.783-.664-1.205-1.609-1.264-2.834"/><path id="text5649" fill="#181717" fill-rule="nonzero" d="M157.8 93.833h1.926c1.115 0 1.921-.299 2.418-.896.502-.603.753-1.578.753-2.925 0-1.341-.232-2.327-.698-2.957-.465-.63-1.191-.945-2.18-.945h-2.22v7.723M155.447 96V84h4.574c1.791 0 3.125.497 4.003 1.49.882.995 1.324 2.502 1.324 4.522 0 1.097-.164 2.064-.492 2.9-.322.837-.793 1.51-1.411 2.02-.465.381-.994.655-1.585.824-.592.163-1.422.244-2.49.244h-3.923"/><path id="text5653" fill="#181717" fill-rule="nonzero" d="M196.04 96V84h7.92v2.086h-5.617v2.72h4.924v2.086h-4.924V96h-2.303"/><path id="text5657" fill="#181717" fill-rule="nonzero" d="M243.628 94.338c-.436.572-.934.992-1.493 1.26-.554.268-1.206.402-1.956.402-1.645 0-2.978-.552-4-1.655-1.017-1.108-1.526-2.561-1.526-4.36 0-1.815.509-3.266 1.526-4.354 1.016-1.087 2.371-1.631 4.064-1.631 1.474 0 2.677.343 3.609 1.028.931.68 1.485 1.619 1.66 2.815h-2.467c-.149-.593-.455-1.044-.918-1.353-.458-.31-1.057-.464-1.796-.464-.985 0-1.76.35-2.324 1.051-.559.696-.838 1.66-.838 2.892 0 1.237.292 2.206.878 2.907.585.701 1.39 1.052 2.411 1.052.767 0 1.408-.22 1.925-.657.516-.439.835-1.039.958-1.802h-2.667v-1.948h4.87v6.162h-1.62l-.296-1.345"/><path id="text5661" fill="#181717" fill-rule="nonzero" d="M275.248 96V84h2.479v4.473h4.942V84h2.48v12h-2.48v-5.32h-4.942V96h-2.48"/><path id="text5665" fill="#181717" fill-rule="nonzero" d="M316.832 91.324h2.155v1.41c0 .407.113.72.339.936.225.211.55.317.971.317.461 0 .782-.13.964-.389.181-.259.272-.745.272-1.458V84h2.23v8.251c0 .74-.042 1.284-.126 1.633-.078.343-.21.647-.397.911-.284.391-.674.69-1.17.896-.495.206-1.076.309-1.744.309-.613 0-1.155-.09-1.626-.27a2.949 2.949 0 01-1.177-.8 2.594 2.594 0 01-.537-.951c-.103-.354-.154-.935-.154-1.744v-.911"/><path id="text5669" fill="#181717" fill-rule="nonzero" d="M355.446 96V84h2.29v4.929L362.08 84h2.865l-4.444 4.855L365.347 96h-2.797l-3.628-5.49-1.186 1.27V96h-2.29"/><path id="text5673" fill="#181717" fill-rule="nonzero" d="M396.04 96V84h2.348v9.784h5.572V96h-7.92"/><path id="text5679" fill="#181717" fill-rule="nonzero" d="M96.04 136v-2.086l6.105-7.739h-5.983V124h8.788v2.086l-6.12 7.747h5.983V136H96.04"/><path id="text5683" fill="#181717" fill-rule="nonzero" d="M134.653 136l3.521-6.126-3.52-5.874h2.661l2.289 4.196 2.274-4.196h2.676l-3.52 5.857 3.52 6.143h-2.661l-2.289-4.155-2.289 4.155h-2.662"/><path id="text5687" fill="#181717" fill-rule="nonzero" d="M185.149 131.77c-.092 1.294-.616 2.323-1.573 3.086-.95.763-2.193 1.144-3.727 1.144-1.767 0-3.142-.526-4.125-1.577-.978-1.052-1.467-2.526-1.467-4.423 0-1.938.5-3.423 1.5-4.454.999-1.03 2.436-1.546 4.31-1.546 1.524 0 2.726.356 3.607 1.067.886.706 1.372 1.706 1.458 3h-2.423c-.102-.644-.378-1.134-.826-1.47-.449-.34-1.054-.51-1.815-.51-1.08 0-1.902.333-2.464.998s-.843 1.637-.843 2.915c0 1.237.279 2.193.835 2.869.562.675 1.359 1.012 2.39 1.012.746 0 1.357-.18 1.832-.54.475-.367.783-.89.924-1.57h2.407"/><path id="text5691" fill="#181717" fill-rule="nonzero" d="M218.72 136l-3.869-12h2.56l2.383 8.978 2.43-8.978h2.528l-3.823 12h-2.209"/><path id="text5695" fill="#181717" fill-rule="nonzero" d="M257.822 133.833h2.943c.694 0 1.2-.125 1.516-.375.317-.25.476-.643.476-1.181 0-.56-.156-.961-.468-1.206-.311-.25-.825-.374-1.54-.374h-2.927v3.136m0-5.19h2.836c.612 0 1.06-.103 1.344-.309.285-.212.427-.543.427-.994 0-.44-.14-.758-.418-.953-.274-.201-.73-.301-1.37-.301h-2.819v2.558M255.446 136v-12h5.688c1.235 0 2.174.26 2.82.782.65.516.975 1.268.975 2.257 0 .608-.12 1.116-.361 1.523-.24.407-.601.722-1.082.945.623.25 1.087.611 1.393 1.084.312.467.468 1.05.468 1.751 0 1.168-.383 2.07-1.148 2.705-.765.635-1.85.953-3.254.953h-5.5"/><path id="text5699" fill="#181717" fill-rule="nonzero" d="M295.05 136v-12h2.585l4.877 8.244V124h2.438v12h-2.552l-4.91-8.244V136h-2.438"/><path id="text5703" fill="#181717" fill-rule="nonzero" d="M333.663 136v-12h3.597l2.352 9.132 2.32-9.132h3.613v12h-2.28v-9.678l-2.4 9.678h-2.49l-2.432-9.678V136h-2.28"/><path id="text5725" fill="#181717" fill-rule="nonzero" d="M438.614 89.363V87h1.98v2.363h-1.98m0 5.974v-2.348h1.98v2.348h-1.98m.013 13.99v-2.362h1.967v2.363h-1.967m0 8.672v-.912c.366-.125.636-.318.81-.578.175-.26.262-.597.262-1.013v-.195h-1.085v-2.348h1.98v2.254c0 .78-.166 1.404-.497 1.872-.33.473-.82.78-1.47.92"/><path id="text5731" fill="#181717" fill-rule="nonzero" d="M480.71 85h1.468v4.301h-1.468V85m-2.492 0h1.468v4.301h-1.468V85m1.254 19.699h1.468V109h-1.468v-4.301"/><path id="text5737" fill="#181717" fill-rule="nonzero" d="M514.851 93.804V86h5.524v1.356h-3.979v1.664h3.638v1.335h-3.638v2.008h4.165v1.441h-5.71m6.775 0v-5.759h1.472v.683c.21-.279.458-.487.744-.625.286-.141.605-.212.956-.212.62 0 1.084.166 1.39.498.31.329.465.825.465 1.49v3.925h-1.503v-3.48c0-.414-.07-.709-.207-.886-.134-.176-.355-.264-.662-.264-.354 0-.635.109-.842.328-.206.215-.31.51-.31.885v3.417h-1.503m8.82.042l-.496.016c-.19.01-.31.016-.362.016-.575 0-.97-.11-1.183-.328-.21-.223-.315-.66-.315-1.309v-3.115h-.744v-1.081h.744v-1.573h1.488v1.573h.868v1.08h-.868v3.19c0 .152.032.253.098.302.065.046.2.07.403.07h.367v1.16m4.335-1.791h1.499c-.152.607-.459 1.084-.92 1.43-.458.343-1.018.514-1.68.514-.813 0-1.457-.279-1.932-.837-.475-.562-.713-1.321-.713-2.278 0-.943.234-1.687.703-2.23.468-.545 1.109-.817 1.922-.817.861 0 1.526.269 1.994.806.469.533.703 1.294.703 2.283a4.44 4.44 0 01-.015.403h-3.752c.02.452.128.793.32 1.022.197.23.478.345.843.345.258 0 .47-.052.635-.154.166-.106.297-.268.393-.487m-2.19-1.738h2.21c-.013-.389-.115-.684-.304-.885-.186-.205-.455-.307-.806-.307-.327 0-.586.102-.775.307-.186.205-.295.5-.326.885m4.764 3.486v-5.759h1.395v.985c.197-.395.426-.685.688-.868.261-.188.573-.281.935-.281.058 0 .103.002.134.005.035 0 .062.002.083.005l.005 1.563h-.501c-.41 0-.718.11-.925.329-.207.219-.31.544-.31.975v3.046h-1.504"/><path id="path5745" fill="#181717" d="M524.752 115v-10l-9.9 5z"/><path id="path5747" stroke="#181717" stroke-width="3" d="M539.604 100v10h-19.802"/><g id="g5790" transform="translate(6.901 120)"><text id="tspan5755" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x=".208" y="17.237">Shift</tspan></text><path id="path5757" stroke="#181717" stroke-width="2" d="M8.16 19.535l-7.494 10h4.996v5h4.996v-5h4.996z"/></g><g id="g5795" transform="translate(495.05 127)"><g id="text5797" fill="#181717" fill-rule="nonzero" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal" transform="translate(30.402)"><text id="tspan5799"><tspan x=".537" y="17.955">Shift</tspan></text></g><path id="path5801" stroke="#181717" stroke-width="2" d="M7.55 10.628L0 20.58h5.033v4.976h5.034V20.58H15.1z"/></g><path id="text5803" fill="#181717" fill-rule="nonzero" d="M384.158 126v1.71l-6.755 2.848 6.755 2.841v1.71l-8.91-3.786v-1.553l8.91-3.77m-5.605 32v-.913c.424-.125.736-.318.938-.578.202-.26.303-.598.303-1.014v-.195h-1.24v-2.35h2.291v2.256c0 .775-.194 1.4-.582 1.873-.384.474-.953.78-1.71.921"/><path id="text5813" fill="#181717" fill-rule="nonzero" d="M415.842 127l8.91 3.731v1.538l-8.91 3.746v-1.692l6.77-2.812-6.77-2.82V127m3.305 29v-2.325h2.277V156h-2.277"/><path id="text5819" fill="#181717" fill-rule="nonzero" d="M459.343 132.387c0-.056-.002-.131-.008-.224a4.45 4.45 0 01-.008-.217c0-.454.05-.831.151-1.13.101-.305.263-.59.485-.853.17-.196.421-.42.755-.673.339-.253.559-.437.66-.55.211-.233.357-.442.436-.628.08-.185.12-.39.12-.611 0-.49-.141-.873-.422-1.147-.28-.273-.672-.41-1.176-.41-.503 0-.9.165-1.191.496-.286.325-.442.784-.469 1.378h-2.24v-.24c0-1.09.352-1.957 1.056-2.602.71-.65 1.663-.976 2.86-.976 1.224 0 2.196.307 2.916.922.726.609 1.088 1.427 1.088 2.455 0 .361-.042.686-.127.976-.08.283-.204.544-.373.782-.217.3-.554.624-1.009.976-.45.345-.723.56-.818.642a1.828 1.828 0 00-.437.604 2.04 2.04 0 00-.12.891 1.8 1.8 0 01.008.14h-2.137m-.11 3.314v-2.269h2.343v2.27h-2.344M457.897 157l3.941-13.174h1.088L458.97 157h-1.073"/><path id="text5827" fill="#181717" fill-rule="nonzero" d="M11.801 181.18c-.057.863-.387 1.549-.988 2.057-.599.509-1.38.763-2.345.763-1.112 0-1.976-.35-2.595-1.052-.615-.7-.923-1.683-.923-2.948 0-1.292.315-2.282.944-2.97.628-.686 1.532-1.03 2.711-1.03.959 0 1.715.237 2.269.711.557.471.863 1.138.917 2h-1.524c-.064-.43-.238-.756-.52-.979-.282-.227-.663-.34-1.142-.34-.68 0-1.196.221-1.55.665-.353.443-.53 1.09-.53 1.943 0 .825.176 1.462.526 1.912.353.45.854.676 1.503.676.47 0 .853-.12 1.152-.361.3-.244.493-.593.581-1.047h1.514m3.569 2.65c-.136.003-.3.008-.49.015-.187.01-.306.016-.357.016-.567 0-.956-.107-1.167-.32-.207-.216-.31-.64-.31-1.273v-3.03h-.735v-1.052h.734v-1.531h1.468v1.53h.857v1.052h-.857v3.103c0 .148.033.246.097.294.065.045.197.067.398.067h.362v1.129m.861-.041v-5.603h1.376v.958c.194-.385.42-.666.678-.845a1.56 1.56 0 01.923-.273c.058 0 .102.001.133.005.033 0 .06.002.081.005l.005 1.52h-.494c-.405 0-.709.107-.913.32-.204.213-.306.53-.306.949v2.964h-1.483m4.068 0v-7.593h1.483v7.593H20.3"/><path id="text5831" fill="#181717" fill-rule="nonzero" d="M108.714 177.587l-1.097 3.373h2.2l-1.103-3.373m-.946-1.587h1.882l2.855 7.925h-1.71l-.538-1.63h-3.07l-.521 1.63h-1.716l2.818-7.925m5.468 7.925V176h1.565v7.925h-1.565m5.576.043a49.21 49.21 0 00-.516.016c-.197.01-.323.016-.377.016-.598 0-1.009-.111-1.231-.334-.219-.226-.328-.668-.328-1.328v-3.164h-.774v-1.097h.774v-1.598h1.549v1.598h.903v1.097h-.903v3.239c0 .154.034.256.102.307.068.046.208.07.42.07h.38v1.178"/><path id="text5835" fill="#181717" fill-rule="nonzero" d="M408.714 177.587l-1.097 3.373h2.2l-1.103-3.373m-.946-1.587h1.882l2.855 7.925h-1.71l-.538-1.63h-3.07l-.521 1.63h-1.716l2.818-7.925m5.468 7.925V176h1.565v7.925h-1.565m5.576.043a49.21 49.21 0 00-.516.016c-.197.01-.323.016-.377.016-.598 0-1.009-.111-1.231-.334-.219-.226-.328-.668-.328-1.328v-3.164h-.774v-1.097h.774v-1.598h1.549v1.598h.903v1.097h-.903v3.239c0 .154.034.256.102.307.068.046.208.07.42.07h.38v1.178"/><path id="text5839" fill="#181717" fill-rule="nonzero" d="M552.395 181.18c-.057.863-.387 1.549-.988 2.057-.599.509-1.38.763-2.345.763-1.111 0-1.976-.35-2.595-1.052-.615-.7-.922-1.683-.922-2.948 0-1.292.314-2.282.943-2.97.628-.686 1.532-1.03 2.711-1.03.959 0 1.715.237 2.269.711.557.471.863 1.138.917 2h-1.524c-.064-.43-.238-.756-.52-.979-.282-.227-.662-.34-1.142-.34-.68 0-1.196.221-1.55.665-.353.443-.53 1.09-.53 1.943 0 .825.176 1.462.526 1.912.353.45.854.676 1.503.676.47 0 .853-.12 1.152-.361.3-.244.493-.593.582-1.047h1.513m3.569 2.65c-.136.003-.3.008-.49.015-.187.01-.306.016-.356.016-.568 0-.957-.107-1.168-.32-.207-.216-.31-.64-.31-1.273v-3.03h-.735v-1.052h.734v-1.531h1.468v1.53h.857v1.052h-.857v3.103c0 .148.033.246.097.294.065.045.197.067.398.067h.362v1.129m.861-.041v-5.603h1.376v.958c.194-.385.42-.666.678-.845a1.56 1.56 0 01.923-.273c.058 0 .102.001.133.005.034 0 .06.002.081.005l.005 1.52h-.494c-.405 0-.709.107-.913.32-.204.213-.306.53-.306.949v2.964h-1.483m4.068 0v-7.593h1.483v7.593h-1.483"/><path id="text5872" fill="#181717" fill-rule="nonzero" d="M72.458 176.567L70.297 169h1.608l1.29 5.338 1.09-5.338h1.655l1.09 5.338L78.32 169h1.593l-2.156 7.567h-1.48l-1.167-5.795-1.172 5.795h-1.48m8.228-6.206V169h1.49v1.361h-1.49m0 6.206v-5.584h1.49v5.584h-1.49m2.816 0v-5.584h1.46v.663c.208-.27.453-.473.737-.606.283-.137.599-.206.947-.206.614 0 1.074.161 1.377.483.308.318.461.8.461 1.444v3.806h-1.49v-3.375c0-.4-.068-.687-.205-.858-.133-.171-.351-.257-.655-.257-.352 0-.63.106-.835.319-.204.209-.307.495-.307.858v3.313h-1.49m-12.55 13.152v-7.567h1.552v3.108l2.944-3.108h1.94l-3.01 3.062 3.282 4.505h-1.895l-2.457-3.463-.804.802v2.661h-1.552m10.676-1.695h1.485c-.15.589-.454 1.051-.911 1.387-.454.332-1.009.498-1.664.498-.806 0-1.444-.27-1.915-.812-.471-.544-.707-1.28-.707-2.209 0-.914.232-1.635.696-2.163.465-.527 1.1-.79 1.905-.79.853 0 1.512.26 1.977.78.464.517.696 1.255.696 2.214a4.215 4.215 0 01-.015.39h-3.718c.02.44.126.77.318.992.194.223.472.334.834.334.256 0 .466-.05.63-.149.164-.102.294-.26.39-.472m-2.172-1.685h2.192c-.014-.377-.115-.663-.302-.858-.185-.199-.451-.298-.8-.298-.323 0-.58.099-.767.298-.185.198-.292.484-.323.858m4.941 5.62v-1.207a1.976 1.976 0 00.348.02c.294 0 .514-.07.661-.21.147-.137.22-.345.22-.622a.799.799 0 00-.03-.154l-2.008-5.651h1.639l1.172 4.027 1.142-4.027h1.567l-2.289 6.586c-.17.493-.384.829-.64 1.007-.256.181-.64.272-1.152.272-.092 0-.191-.003-.297-.01a6.338 6.338 0 01-.333-.031"/><path id="text5878" fill="#181717" fill-rule="nonzero" d="M472.458 176.567L470.297 169h1.608l1.29 5.338 1.09-5.338h1.655l1.09 5.338 1.29-5.338h1.593l-2.156 7.567h-1.48l-1.167-5.795-1.172 5.795h-1.48m8.228-6.206V169h1.49v1.361h-1.49m0 6.206v-5.584h1.49v5.584h-1.49m2.816 0v-5.584h1.46v.663c.208-.27.453-.473.737-.606.283-.137.599-.206.947-.206.614 0 1.074.161 1.377.483.308.318.461.8.461 1.444v3.806h-1.49v-3.375c0-.4-.068-.687-.205-.858-.133-.171-.351-.257-.655-.257-.352 0-.63.106-.835.319-.204.209-.307.495-.307.858v3.313h-1.49m-12.55 13.152v-7.567h1.552v3.108l2.944-3.108h1.94l-3.01 3.062 3.282 4.505h-1.895l-2.457-3.463-.804.802v2.661h-1.552m10.676-1.695h1.485c-.15.589-.454 1.051-.911 1.387-.454.332-1.009.498-1.664.498-.806 0-1.444-.27-1.915-.812-.471-.544-.707-1.28-.707-2.209 0-.914.232-1.635.696-2.163.465-.527 1.1-.79 1.905-.79.853 0 1.512.26 1.977.78.464.517.696 1.255.696 2.214a4.215 4.215 0 01-.015.39h-3.718c.02.44.126.77.318.992.194.223.472.334.834.334.256 0 .466-.05.63-.149.164-.102.294-.26.39-.472m-2.172-1.685h2.192c-.014-.377-.115-.663-.302-.858-.185-.199-.451-.298-.8-.298-.323 0-.58.099-.767.298-.185.198-.292.484-.323.858m4.941 5.62v-1.207a1.976 1.976 0 00.348.02c.294 0 .514-.07.661-.21.147-.137.22-.345.22-.622a.8.8 0 00-.03-.154l-2.008-5.651h1.639l1.172 4.027 1.142-4.027h1.567l-2.289 6.586c-.17.493-.384.829-.64 1.007-.256.181-.64.272-1.152.272-.092 0-.191-.003-.297-.01a6.338 6.338 0 01-.333-.031"/><path id="text5884" fill="#181717" fill-rule="nonzero" d="M506.93 183.804V176h2.338l1.529 5.94 1.507-5.94h2.348v7.804h-1.482v-6.294l-1.56 6.294h-1.617l-1.58-6.294v6.294h-1.482m12.693-1.748h1.513c-.153.607-.463 1.084-.929 1.43-.462.343-1.027.514-1.695.514-.821 0-1.471-.279-1.951-.837-.48-.562-.72-1.321-.72-2.278 0-.943.236-1.687.71-2.23.472-.545 1.12-.817 1.94-.817.87 0 1.54.269 2.014.806.473.533.71 1.294.71 2.283a4.4 4.4 0 01-.016.403h-3.788c.02.452.129.793.323 1.022.199.23.482.345.85.345.262 0 .476-.052.643-.154.166-.106.299-.268.396-.487m-2.212-1.738h2.233c-.014-.389-.117-.684-.308-.885-.188-.205-.459-.307-.814-.307-.33 0-.591.102-.782.307-.188.205-.298.5-.33.885m4.811 3.486v-5.759h1.487v.683a2.05 2.05 0 01.751-.625c.289-.141.61-.212.965-.212.627 0 1.094.166 1.404.498.313.329.47.825.47 1.49v3.925h-1.519v-3.48c0-.414-.07-.709-.208-.886-.136-.176-.359-.264-.668-.264-.358 0-.642.109-.85.328-.21.215-.314.51-.314.885v3.417h-1.518m11.441-5.759v5.759h-1.487v-.683c-.215.279-.467.489-.756.63a2.191 2.191 0 01-.965.207c-.623 0-1.09-.166-1.404-.498-.31-.332-.464-.829-.464-1.49v-3.925h1.518v3.48c0 .41.068.704.204.88.135.173.36.26.673.26.355 0 .636-.108.845-.323.212-.22.318-.516.318-.89v-3.407h1.518"/><circle id="Oval" cx="109.168" cy="139" r="26" stroke="#C06334" stroke-width="5"/></g></g></svg> \ No newline at end of file diff --git a/2-ui/3-event-details/8-onscroll/article.md b/2-ui/3-event-details/8-onscroll/article.md index 846924a96c..8de895a40b 100644 --- a/2-ui/3-event-details/8-onscroll/article.md +++ b/2-ui/3-event-details/8-onscroll/article.md @@ -8,7 +8,7 @@ [cut] -ここに現在のスクロールを表示する小さい関数がります: +ここに現在のスクロールを表示する小さい関数があります: ```js autorun window.addEventListener('scroll', function() { diff --git a/2-ui/4-forms-controls/1-form-elements/article.md b/2-ui/4-forms-controls/1-form-elements/article.md index d7410f3eaa..fe9e3f23c8 100644 --- a/2-ui/4-forms-controls/1-form-elements/article.md +++ b/2-ui/4-forms-controls/1-form-elements/article.md @@ -129,7 +129,7 @@ alert(ageElems[0].value); // 10, 1つ目の input の値 これはその図です: -![](form-navigation.png) +![](form-navigation.svg) 例: diff --git a/2-ui/4-forms-controls/1-form-elements/form-navigation.png b/2-ui/4-forms-controls/1-form-elements/form-navigation.png deleted file mode 100644 index 5d00632b2c..0000000000 Binary files a/2-ui/4-forms-controls/1-form-elements/form-navigation.png and /dev/null differ diff --git a/2-ui/4-forms-controls/1-form-elements/form-navigation.svg b/2-ui/4-forms-controls/1-form-elements/form-navigation.svg new file mode 100644 index 0000000000..2c9080703d --- /dev/null +++ b/2-ui/4-forms-controls/1-form-elements/form-navigation.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="463" height="150" viewBox="0 0 463 150"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="form-navigation.svg"><path id="Rectangle-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M174 14h78v28h-78z"/><text id="form" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="197.204" y="33">form</tspan></text><path id="Rectangle-6-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M32 109h101v28H32z"/><text id="elements[0]" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="37.3" y="127">elements[0]</tspan></text><path id="Rectangle-6-Copy-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M162 109h101v28H162z"/><text id="elements[1]" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="167.3" y="127">elements[1]</tspan></text><path id="Rectangle-6-Copy-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M342 109h101v28H342z"/><text id="elements[n]" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="347.3" y="127">elements[n]</tspan></text><text id="..." fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="292.4" y="129">...</tspan></text><path id="Line-Copy-4" fill="#C06334" fill-rule="nonzero" d="M231.968 47.19l.921.389L335.992 91.13l2.335-5.527L348.5 97.5l-15.62 1 2.334-5.527-103.103-43.552-.921-.389.778-1.842z"/><path id="Line-Copy-6" fill="#C06334" fill-rule="nonzero" d="M371.33 91.23l.92.39 2.764 1.167.921.389-.778 1.842-.921-.389-2.764-1.167-.92-.39.777-1.842zm-8.291-3.502l.92.39 2.764 1.167.922.389-.779 1.842-.92-.389-2.764-1.167-.922-.39.779-1.842zm-8.291-3.502l.921.39 3.685 1.556-.778 1.842-.922-.39-2.763-1.166-.921-.39.778-1.842zm-8.29-3.502l.92.39 2.764 1.166.921.39-.778 1.842-4.606-1.946.778-1.842zm-8.291-3.502l.92.389 2.764 1.167.922.39-.779 1.842-.92-.39-2.764-1.167-.922-.389.779-1.842zm-8.291-3.502l.921.389 2.764 1.167.92.39-.777 1.842-.922-.39-2.763-1.167-.921-.389.778-1.842zm-8.29-3.502l.92.389 2.764 1.167.921.39-.778 1.842-4.606-1.946.778-1.842zm-8.291-3.502l.92.389 2.764 1.167.922.39-.779 1.842-.92-.39-2.764-1.167-.922-.389.779-1.842zm-8.291-3.502l.921.389 2.764 1.167.92.39-.777 1.841-.922-.389-2.763-1.167-.921-.39.778-1.841zm-8.29-3.503l3.684 1.557.921.389-.778 1.842-.921-.389-2.764-1.167-.921-.39.778-1.842zm-8.291-3.502l.92.39 2.764 1.167.921.389-.778 1.842-.92-.389-2.765-1.167-.92-.39.778-1.842zM276.12 44.5l-2.335 5.527 1.74.736.922.389-.778 1.842-.921-.389-1.741-.736-2.334 5.528L260.5 45.5l15.62-1zm4.012 8.208l.921.39 3.685 1.556-.778 1.842-.922-.389-2.763-1.167-.921-.39.778-1.842z"/><path id="Line-Copy-5" fill="#C06334" fill-rule="nonzero" d="M186.874 47.136l.99 1.738-.869.495-73.837 42.068 2.971 5.214-15.629.849 8.699-13.013 2.969 5.213 73.837-42.069.869-.495z"/><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M64.441 98.61l1.014 1.725-.862.507-2.586 1.52-.862.507-1.014-1.724.862-.507 2.586-1.52.862-.507zm7.759-4.56l1.013 1.723-.862.507-2.586 1.52-.862.507-1.013-1.724 3.448-2.027.862-.507zm7.758-4.562l1.014 1.724-3.448 2.027-.862.507-1.014-1.724.862-.507 2.586-1.52.862-.507zm7.759-4.562l1.013 1.724-.862.507-2.586 1.52-.862.507-1.014-1.724.862-.506 2.587-1.521.862-.507zm7.758-4.561l1.014 1.724-3.449 2.027-.862.507-1.013-1.724.862-.507 3.448-2.027zm7.758-4.562l1.014 1.724-.862.507-2.586 1.52-.862.507-1.014-1.724.862-.506 2.586-1.52.862-.508zm7.759-4.561l1.013 1.724-4.31 2.534-1.013-1.724.862-.507 3.448-2.027zm7.758-4.562l1.014 1.724-.862.507-2.586 1.52-.862.508-1.014-1.725.862-.506 2.586-1.52.862-.508zm7.759-4.561l1.013 1.724-4.31 2.534-1.014-1.724.862-.507 3.449-2.027zm7.758-4.562l1.014 1.724-.862.507-2.586 1.52-.863.508-1.013-1.725.862-.506 2.586-1.52.862-.508zm7.758-4.561l1.014 1.724-.862.507-2.586 1.52-.862.507-1.014-1.724 4.31-2.534zM161 43l-8.52 13.13-3.042-5.173-2.089 1.229-.862.507-1.013-1.724.862-.507 2.088-1.229-3.04-5.172L161 43z"/><text id="form" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="78.7" y="61">form</tspan></text><text id="form-copy" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="319.7" y="61">form</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M218.5 50.5v37h6l-7 14-7-14h6v-37h2z"/><path id="Line-Copy-3" fill="#C06334" fill-rule="nonzero" d="M203.5 99.5v3h-2v-3h2zm0-9v5h-2v-5h2zm0-9v5h-2v-5h2zm0-9v5h-2v-5h2zm-1-21l7 14h-6v3h-2v-3h-6l7-14z"/></g></g></svg> \ No newline at end of file diff --git a/2-ui/4-forms-controls/1-form-elements/form-navigation@2x.png b/2-ui/4-forms-controls/1-form-elements/form-navigation@2x.png deleted file mode 100644 index 1997b2b360..0000000000 Binary files a/2-ui/4-forms-controls/1-form-elements/form-navigation@2x.png and /dev/null differ diff --git a/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/solution.md b/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/solution.md index e4aeedc102..e8d4f26682 100644 --- a/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/solution.md +++ b/2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/solution.md @@ -1,6 +1,6 @@ `mouse.onclick` を使用してクリックを処理し、ネズミを `position:fixed` で移動可能にし、その後 `mouse.onkeydown` で矢印キーを処理します。 -唯一の落とし穴は `keydown` はフォーカスのある要素でのみトリガするということです。そのため、要素に `tabindex` を追加する必要があります。HTML を変更することは禁止しているので、そのために `mouse.tebIndex` プロパティを使います。 +唯一の落とし穴は `keydown` はフォーカスのある要素でのみトリガするということです。そのため、要素に `tabindex` を追加する必要があります。HTML を変更することは禁止しているので、そのために `mouse.tabIndex` プロパティを使います。 P.S. `mouse.onclick` を `mouse.onfocus` に置き換えることもできます。 diff --git a/2-ui/4-forms-controls/2-focus-blur/article.md b/2-ui/4-forms-controls/2-focus-blur/article.md index a916b7c0d1..56896dfa1d 100644 --- a/2-ui/4-forms-controls/2-focus-blur/article.md +++ b/2-ui/4-forms-controls/2-focus-blur/article.md @@ -14,14 +14,14 @@ ## イベント focus/blur -`focus` イベントはフォーカス時に呼ばれ、`blue` は要素がフォーカスを失ったときに呼ばれます。 +`focus` イベントはフォーカス時に呼ばれ、`blur` は要素がフォーカスを失ったときに呼ばれます。 インプットフィールドのバリデーションでそれらを使ってみましょう。 以下の例では: -- `blue` ハンドラは email が入力されたかどうかをチェックし、もしそうでなければエラーを表示します。 -- `focus` ハンドラはエラーメッセージを隠します(`blue` 時にサイドチェックされます)。: +- `blur` ハンドラは email が入力されたかどうかをチェックし、もしそうでなければエラーを表示します。 +- `focus` ハンドラはエラーメッセージを隠します(`blur` 時にサイドチェックされます)。: ```html run autorun height=60 <style> @@ -87,7 +87,7 @@ Your email please: <input type="email" id="input"> これは、Firefox ([bug](https://bugzilla.mozilla.org/show_bug.cgi?id=53579)) を除いたすべてのブラウザで動作します。 -入力域に何かを入力した後、`key:Tab` や クリックで `<input>` から離れようとすると、`onblue` はフォーカスを戻します。 +入力域に何かを入力した後、`key:Tab` や クリックで `<input>` から離れようとすると、`onblur` はフォーカスを戻します。 `onblur` の中で `event.preventDefault()` を呼び出すことで "フォーカスを失うことを防ぐ" ことはできない点に注意してください。なえなら、`onblur` は要素がフォーカスを失った *後* に動作するためです。 @@ -96,7 +96,7 @@ Your email please: <input type="email" id="input"> その1つは、訪問者がどこか他の場所をクリックした時です。しかし JavaScript 自体もそれを引き起こす可能性があります。例えば: -- `alert` はフォーカスを自身へ移動させるため、フォーカス解除(`blue`)が要素で起きます。また `alert` が消えたとき、フォーカスが戻ります(`focus` イベント)。 +- `alert` はフォーカスを自身へ移動させるため、フォーカス解除(`blur`)が要素で起きます。また `alert` が消えたとき、フォーカスが戻ります(`focus` イベント)。 - もしも要素が DOM から削除された場合も、フォーカス解除が起こります。後で再挿入したとしても、フォーカスは戻りません。 これらの特徴により、`focus/blur` ハンドラが必要ないときにトリガすることがあります。 diff --git a/2-ui/5-loading/01-onload-ondomcontentloaded/article.md b/2-ui/5-loading/01-onload-ondomcontentloaded/article.md new file mode 100644 index 0000000000..f1a88f5c33 --- /dev/null +++ b/2-ui/5-loading/01-onload-ondomcontentloaded/article.md @@ -0,0 +1,245 @@ +# ページのライフサイクル: DOMContentLoaded, load, beforeunload, unload + +HTML ページのライフサイクルは、3つの重要なイベントを持っています: + +- `DOMContentLoaded` -- ブラウザが HTML を完全に読み込み、DOM ツリーは構築されましたが、写真 `<img>` のような外部リソースやスタイルシートはまだ読み込まれていない可能性があります。 +- `load` -- ブラウザがすべてのリソース(画像, スタイルなど)を読み込みました。 +- `beforeunload/unload` -- ユーザがページを離れようとしているとき。 + +それぞれのイベントが役に立つ場合があります: + +- `DOMContentLoaded` イベント -- DOMは準備できたので、ハンドラは DOM ノードを調べ、インタフェースを初期化することができます。 +- `load` イベント -- 追加のリソースがロードされ、画像のサイズなどが取得できます(HTML/CSSで指定されていない場合)。 +- `beforeunload/unload` イベント -- ユーザが離れようとしており、ユーザが行ったページ上での変更を保存するかを確認したり、本当に離れたいかを尋ねることができます。 + +これらのイベントの詳細について見ていきましょう。 + +[cut] + +## DOMContentLoaded + +`DOMContentLoaded` イベントは `document` オブジェクトで発生します。 + +キャッチするためには `addEventListener` を使わなければなりません: + +```js +document.addEventListener("DOMContentLoaded", ready); +``` + +例: + +```html run height=200 refresh +<script> + function ready() { + alert('DOM is ready'); + + // イメージはまだロードされていません(キャッシュされてない限り), なのでサイズは 0x0 です + alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`); + } + +*!* + document.addEventListener("DOMContentLoaded", ready); +*/!* +</script> + +<img id="img" src="https://en.js.cx/clipart/train.gif?speed=1&cache=0"> +``` + +例では、`DOMContentLoaded` ハンドラはドキュメントがロードされたときに実行され、ページ読み込みは待ちません。したがって、`alert` で表示されるサイズはゼロです。 + +一見すると、`DOMContentLoaded` イベントはとてもシンプルです。DOM ツリーが準備できた -- ここのイベントです。しかし、特徴はほとんどありません。 + +### DOMContentLoaded と scripts + +ブラウザが最初にHTMLをロードし、テキスト中の `<script>...</script>` に出くわすと、DOM の構築を続けることができません。すぐにスクリプトを実行する必要があります。そのため、このようなスクリプトがすべて実行された後にのみ `DOMContentLoaded` は発生する可能性があります。 + +外部スクリプト (`src` を持つもの) も、スクリプトがロードされ実行されている間はDOM の構築は一時停止します。したがって、`DOMContentLoaded` は外部スクリプトも待ちます。 + +唯一の例外は、`async` や `defer` 属性をもつ外部スクリプトです。これらは、ブラウザにそのスクリプトは待たずに処理を続けるよう言います。なので、ユーザはスクリプトのロードが終わる前にページを見ることができ、パフォーマンスに良いです。 + +```smart header="`async` と `defer` に関する言葉" +属性 `async` と `defer` は外部スクリプトに対してのみ動作します。`src` がない場合には無視されます。 + +どちらも、ブラウザにページでの処理をつづけるよう言い、"バックグラウンド" でスクリプトをロードします。その後、ロードできたらスクリプトを実行します。したがって、スクリプトは DOM の構築とページレンダリングをブロックしません。 + +そららの間に2つの違いがあります。 + +| | `async` | `defer` | +|---------|---------|---------| +| 順番 | `async` を持つスクリプトは *読み込んだもの順* で実行します。ドキュメント順は関係ありません -- 最初にロードされたものが最初に実行されます。 | `defer` を持つスクリプトは常に *ドキュメント順* で実行されます。 | +| `DOMContentLoaded` | `async` を持つスクリプトは、ドキュメントがまだ完全にダウンロードされていなくても実行されることがあります。これはスクリプトが小さい or キャッシュされており、ドキュメントが十分長い場合に発生します。 | `defer` を持つスクリプトは、ドキュメントがロードされ、パースされた後(必要に応じて待機する)に実行されます。`DOMContentLoaded` の直前です。| + +そのため、`async` は完全に独立したスクリプトに対して使われます。 + +``` + +### DOMContentLoaded と styles + +外部のスタイルシートは DOM には影響しないので、`DOMContentLoaded` はそれらを待ちません。 + +しかし、落とし穴があります: スタイルの後にスクリプトがある場合、そのスクリプトはスタイルシートが実行されるのを待たなければなりません。: + +```html +<link type="text/css" rel="stylesheet" href="style.css"> +<script> + // スクリプトはスタイルシートが読み込まれるまで実行されません + alert(getComputedStyle(document.body).marginTop); +</script> +``` + +この理由は、上の例のように、スクリプトが座標や他のスタイルに依存した要素のプロパティを取得したい場合があるためです。当然、それはスタイルがロードされるのを待たなければいけません。 + +`DOMContentLoaded` がスクリプトを待つので、その前のスタイルも同様に待つことになります。 + +### 組み込みのブラウザの自動入力 + +Firefox, Chrome や Opera の自動入力は `DOMContentLoaded` で起こります。 + +例えば、ページがログインとパスワードのフォームを持っていて、ブラウザがその値を覚えていた場合、 `DOMContentLoaded` でそれらを自動入力しようとする場合があります(ユーザが許可している場合)。 + +なので、読み込みに時間のかかるスクリプトによって `DOMContentLoaded` が延びると、自動入力もまた待ちます。恐らくあなたもサイトによっては見たことがあるでしょう(ブラウザの自動入力を利用している場合) -- ログイン/パスワードフィールドの自動入力がすぐにはされず、ページが完全にロードされるまで遅延があります。実際、それは `DOMContentLoaded` イベントまでの遅延です。 + +外部スクリプトに対して `async` や `defer` を使う小さなメリットの1つは -- `DOMContentLoaded` をブロックせず、ブラウザの自動入力に遅延がないことです。 + +## window.onload + +`window` オブジェクトの `load` イベントはスタイルや画像、その他リソースを含めページ全体が読み込まれたときにトリガされます。 + +下の例では、`window.onload` がすべての画像を待つため、画像サイズを正しく表示します。: + +```html run height=200 refresh +<script> + window.onload = function() { + alert('Page loaded'); + + // この時、画像はロードされています + alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`); + }; +</script> + +<img id="img" src="https://en.js.cx/clipart/train.gif?speed=1&cache=0"> +``` + +## window.onunload + +訪問者がページを離れるとき、`unload` イベントが `window` でトリガされます。そこでは、関連するポップアップウィンドウを閉じるなど、遅延なく何かをすることができます。しかし、別のページへの遷移をキャンセルすることはできません。 + +そのためには、別のイベント `onbeforeunload` を使う必要があります。 + +## window.onbeforeunload + +訪問者がページを離れ始めたり、ウィンドウを閉じようとした場合、`beforeunload` ハンドラで追加の確認を尋ねることができます。 + +質問の文字列を返す必要があります。ブラウザはそれを表示します。 + +例えば: + +```js +window.onbeforeunload = function() { + return "There are unsaved changes. Leave now?"; +}; +``` + +```online +下の `<iframe>` でハンドラを設定するためにボタンをクリックしてください。その後、そのハンドラの実行を見るためにリンクをクリックしてみてください。: + +[iframe src="window-onbeforeunload" border="1" height="80" link edit] +``` + +```warn header="テキストを無視し、代わりに独自のメッセージを表示するブラウザもあります" +Chrome や Firefox のような一部のブラウザは文字列を無視し、代わりに独自のメッセージを表示します。これは安全のためであり、潜在的な誤解を招くメッセージやハックしたメッセージからユーザを保護するためにそのようになっています。 +``` + +## readyState + +ドキュメントがロードされた後に `DOMContentLoaded` ハンドラを設定すると何が起こるでしょうか? + +当然、実行されません。 + +ドキュメントが準備できたかどうかが定かでない場合があります。例えば `async` 属性を持つ外部スクリプトを読み込み、それが非同期に実行するような場合です。ネットワークに依存して、ドキュメントが完成する前に実行されるかもしれないし、その後かもしれず、定かではありません。従って、我々はドキュメントの現在の状態を知ることができるべきです。 + +`document.readyState` プロパティは我々にその情報を提供します。3つの値を取り得ます。: + +- `"loading"` -- ドキュメントがロード中です。 +- `"interactive"` -- ドキュメントは完全に読み込まれました。 +- `"complete"` -- ドキュメントは完全に読み込まれ、すべてのリソース(画像のような)も読み込まれました。 + +なので、 `document.readyState` をチェックし、ハンドラを設定、もしくはすでに準備出来ている場合はすぐにコードを実行することができます。 + +このように: + +```js +function work() { /*...*/ } + +if (document.readyState == 'loading') { + document.addEventListener('DOMContentLoaded', work); +} else { + work(); +} +``` + +状態が変わったときにトリガされる `readystatechange` イベントがあります。なので、次のようにしてこれらの状態を出力することができます。: + +```js run +// 現在の状態 +console.log(document.readyState); + +// 状態の変化を出力 +document.addEventListener('readystatechange', () => console.log(document.readyState)); +``` + +`readystatechange` イベントはドキュメントの読み込み状態を追跡する代替のメカニズムで、それは昔に登場しました。最近ではほとんど使われていませんが、完全性のために説明しておきます。 + +他のイベントの中で `readystatechange` の場所はどこでしょう? + +タイミングを見るために、ここではイベントを記録する `<iframe>`, `<img>` とハンドラを持つドキュメントがあります。: + +```html +<script> + function log(text) { /* output the time and message */ } + log('initial readyState:' + document.readyState); + + document.addEventListener('readystatechange', () => log('readyState:' + document.readyState)); + document.addEventListener('DOMContentLoaded', () => log('DOMContentLoaded')); + + window.onload = () => log('window onload'); +</script> + +<iframe src="iframe.html" onload="log('iframe onload')"></iframe> + +<img src="http://en.js.cx/clipart/train.gif" id="img"> +<script> + img.onload = () => log('img onload'); +</script> +``` + +動作例は [サンドボックスの中](sandbox:readystate) です。 + +典型的な出力: +1. [1] initial readyState:loading +2. [2] readyState:interactive +3. [2] DOMContentLoaded +4. [3] iframe onload +5. [4] readyState:complete +6. [4] img onload +7. [4] window onload + +角括弧内の数値はそれが発生したおおよその時間を示します。実際の時間はもう少し大きいですが、同じ数字でラベル付けされたイベントは、ほぼ同時に発生します(+- 数ミリ秒)。 + +- `document.readyState` は `DOMContentLoaded` の直前に `interactive` になります。これら2つのイベントは実際には同じことを意味します。 +- `document.readyState` は、すべてのリソース(`iframe` や `img`)がロードされたときに `complete` になります。ここでは、`img.onload` (`img` は最後のリソース) と `window.onload` がほぼ同じ時間に発生していることが分かります。`complete` 状態にスイッチすることは、`window.onload` と同じことを意味します。違いは、`window.onload` は常に他のすべての `load` ハンドラの後で動作するということです。 + +## サマリ + +ページのライフサイクルイベント: + +- `DOMContentLoaded` イベントは DOM が準備できたときに `document` 上でトリガされます。この段階では要素に対して JavaScript を適用することができます。 + - `async` または `defer` もつ外部のものを除いたすべてのスクリプトが実行されます。 + - 画像や他のリソースはまだ読み込み中かもしれません。 +- `window` での `load` イベントはページとすべてのリソースがロードされたときにトリガされます。通常はこんなに長く待つ必要はないため、めったに使われません。 +- `window` での `beforeunload` イベントは、ユーザがページから離れたいときにトリガされます。もし文字列を返すと、ブラウザはユーザが本当に離れたいかどうかの質問を表示します。 +- `window` での `unload` イベントはユーザが最終的に離れるときにトリガされます。ハンドラの中では遅延やユーザへの質問を含まないシンプルなことのみが可能です。その制限があるので、ほとんど使われません。 +- `document.readyState` はドキュメントの現在の状態で、変更は `readystatechange` イベントで追跡することが可能です。: + - `loading` -- ドキュメントは読み込み中です。 + - `interactive` -- ドキュメントはパースされ、`DOMContentLoaded` とほぼ同じ時間に発生しますが、その前に発生します。 + - `complete` -- ドキュメントとリソースが読み込まれました。`window.onload` とほぼ同じ時間に発生しますが、その前に起きます。 diff --git a/2-ui/3-event-details/10-onload-ondomcontentloaded/readystate.view/iframe.html b/2-ui/5-loading/01-onload-ondomcontentloaded/readystate.view/iframe.html similarity index 100% rename from 2-ui/3-event-details/10-onload-ondomcontentloaded/readystate.view/iframe.html rename to 2-ui/5-loading/01-onload-ondomcontentloaded/readystate.view/iframe.html diff --git a/2-ui/3-event-details/10-onload-ondomcontentloaded/readystate.view/index.html b/2-ui/5-loading/01-onload-ondomcontentloaded/readystate.view/index.html similarity index 100% rename from 2-ui/3-event-details/10-onload-ondomcontentloaded/readystate.view/index.html rename to 2-ui/5-loading/01-onload-ondomcontentloaded/readystate.view/index.html diff --git a/2-ui/3-event-details/10-onload-ondomcontentloaded/window-onbeforeunload.view/index.html b/2-ui/5-loading/01-onload-ondomcontentloaded/window-onbeforeunload.view/index.html similarity index 100% rename from 2-ui/3-event-details/10-onload-ondomcontentloaded/window-onbeforeunload.view/index.html rename to 2-ui/5-loading/01-onload-ondomcontentloaded/window-onbeforeunload.view/index.html diff --git a/2-ui/5-loading/02-script-async-defer/article.md b/2-ui/5-loading/02-script-async-defer/article.md new file mode 100644 index 0000000000..e78d9489e8 --- /dev/null +++ b/2-ui/5-loading/02-script-async-defer/article.md @@ -0,0 +1,202 @@ + +# スクリプト: async, defer + +最近の web サイトでは、スクリプトは HTML よりも "重い" ことがしばしばです: ダウンロードサイズはより大きく、処理時間も長くなります。 + +ブラウザが HTML をロードし、`<script>...</script>` タグに遭遇すると、DOM の構築を続けることはできません。すぐにスクリプトを実行する必要があります。外部スクリプト `<script src="..."></script>` についても同じです: ブラウザはスクリプトをダウンロードし、それを実行するまで待つ必要があり、その後にページの残り部分の処理をすることになります。 + +これは2つの重要な問題につながります: + +1. スクリプトは、それ以降の DOM要素は認識することができないため、ハンドラーを追加したりすることはできません。 +2. ページの先頭に重いスクリプトがあると、"ページをブロック" します。利用者はそれがダウンロードされ実行されるまでページコンテンツを見ることができません: + +```html run height=100 +<p>...content before script...</p> + +<script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script> + +<!-- スクリプトがロードされるまで表示されません --> +<p>...content after script...</p> +``` + +回避策はいくつかあります。例えば、ページの末尾にスクリプトを置きます。すると要素を表示でき、ページコンテンツの表示をブロックしません: + +```html run +<body> + ...all content is above the script... + + <script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script> +</body> +``` + +ですが、この方法は完璧には程遠いです。例えば、ブラウザが完全なHTMLドキュメントをダウンロードした後にのみスクリプトに気づき(、ダウロードが開始でき)ます。HTML ドキュメントが長いと、かなりの遅延になる可能性があります。 + +このようなことは、十分に早い接続を使用している人々には見えませんが、世界中の多くの人は依然として低速のインターネットを利用しており、完璧とはほど遠いモバイルインターネット接続を使用しています。 + +幸いなことに、この問題を解決する2つの `<script>` 属性があります: `defer` と `async` です。 + +## defer + +`defer` 属性はブラウザにスクリプトを待たないよう指示します。代わりに、ブラウザは HTML の処理を継続し、DOM を構築します。スクリプトは "バックグラウンド" でロードされ、DOM が完全に構築されたときに実行されます。 + +これは上記と同じ例ですが、`defer` を指定しています: + +```html run height=100 +<p>...content before script...</p> + +<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script> + +<!-- すぐに表示されます --> +<p>...content after script...</p> +``` + +つまり: + +- `defer` をもつスクリプトはページをブロックしません。 +- `defer` をもつスクリプトは常に DOM は準備できた(ただし、`DOMContentLoaded` イベントの前です)ときに実行されます。 + +次の例は2つ目の部分の例です: + +```html run height=100 +<p>...content before scripts...</p> + +<script> + document.addEventListener('DOMContentLoaded', () => alert("DOM ready after defer!")); +</script> + +<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script> + +<p>...content after scripts...</p> +``` + +1. ページコンテンツはすぐに表示されます。 +2. `DOMContentLoaded` イベントハンドラは遅延スクリプトを待ちます。スクリプトがダウンロードされ実行されたときに実行されます。 + +**遅延スクリプトは通常のスクリプトのように、相対的な順序を維持します。** + +2つの遅延スクリプトがあるとしましょう: `long.js` と `small.js` です: + +```html +<script defer src="https://javascript.info/article/script-async-defer/long.js"></script> +<script defer src="https://javascript.info/article/script-async-defer/small.js"></script> +``` + +ブラウザはページをスキャンしてスクリプトを探し、それらを並列にダウンロードしてパフォーマンスを向上させます。そのため、上の例では両方のスクリプトが並列でダウンロードされます。おそらく `small.js` が最初に終了します。 + +...ですが、`defer` 属性は ブラウザに "ブロックしない" ように指示することに加え、相対的な順序を維持することを保証します。したがって、たとえ `small.js` が最初にロードされた場合でも、`long.js` が実行されるまで待ってから実行します。 + +これは、JavaScript ライブラリを読み込み、次にそれに依存したスクリプトを読み込む必要があるケースでは重要になることがあります。 + +```smart header="`defer` 属性は外部スクリプト専用です" +`src` がない `<script>` タグの場合、`defer` 属性は無視されます。 +``` + +## async + +`async` 属性は `defer` に似ています。これもスクリプトをブロックしませんが、振る舞いに重要な違いがあります。 + +`async` 属性はスクリプトが完全に独立していることを意味します。: + +- ブラウザは `async` スクリプトをブロックしません(`defer` と同じように)。 +- 他のスクリプトは `async` スクリプトを待たず、`async` スクリプトもそれらを待ちません。 +- `DOMContentLoaded` と `async` スクリプトは互いを待ちません: + - `DOMContentLoaded` は `async` スクリプトの前で発生する可能性があります(ページが完了した後に `async` スクリプトの読み込みが終了した場合) + - ...あるいは、`async` スクリプトの後です(非同期スクリプトが短いか HTTPキャッシュにあった場合)。 + +つまり、`async` スクリプトはバックグラウンドで読み込まれ、準備ができたら実行されます。DOM と他のスクリプトはそれらを待たず、それらも何も待ちません。読み込まれたときに実行される完全に独立したスクリプトです。 + +これは `defer` で見たものと同様の例です。:2つのスクリプト `long.js` と `small.js` がありますが、今回は `defer` の代わりに `async` です。 + +これらは互いを待ちません。最初に読み込まれたもの(おそらく `small.js`)が最初に実行されます: + +```html run height=100 +<p>...content before scripts...</p> + +<script> + document.addEventListener('DOMContentLoaded', () => alert("DOM ready!")); +</script> + +<script async src="https://javascript.info/article/script-async-defer/long.js"></script> +<script async src="https://javascript.info/article/script-async-defer/small.js"></script> + +<p>...content after scripts...</p> +``` + +- ページのコンテンツはすぐに表示されます: `async` はブロックしません。 +- `DOMContentLoaded` は `async` の前後の両方で発生する可能性があり、ここでは保証されません。 +- より小さいスクリプト `small.js` が2番目にありますが、おそらく `long.js` の前に読み込まれるので、`small.js` が最初に実行されます。ですが、キャッシュされている場合は、`long.js` が最初に読み込まれ、最初に実行されることもあります。つまり、非同期スクリプトは "ロードファースト" 順で実行します。 + +非同期スクリプトは独立したサードパーティのスクリプトをページに組み込む際に便利です。:カウンター、広告など。それらは我々のスクリプトには依存しないので、我々のスクリプトを待つべきではありません。: + +```html +<!-- Google Analytics は通常このように追加されます --> +<script async src="https://google-analytics.com/analytics.js"></script> +``` + +## ダイナミックスクリプト + +ページにスクリプトを追加する、もう1つの重要な方法があります。 + +JavaScript を利用して動的にスクリプトを作成しドキュメントを追加することが可能です: + +```js run +let script = document.createElement('script'); +script.src = "/article/script-async-defer/long.js"; +document.body.append(script); // (*) +``` + +スクリプトはドキュメントに追加されるとすぐに読み込みを開始します`(*)`。 + +**ダイナミックスクリプトはデフォルトで "非同期" として動作します。** + +つまり: +- これらは何も待たず、逆に何もこれらを待ちません。 +- 最初に読み込まれたスクリプトが最初に実行されます("ロードファースト" 順)。 + + +`script.async=true` を明示的に設定することで変更できます。スクリプトは `defer` のようにドキュメント順に実行されます。 + +この例では、`loadScript(src)` 関数はスクリプトを追加し、`async` を `false` にしています。 + +そのため、`long.js` が常に最初に実行されます(最初に追加されているので): + +```js run +function loadScript(src) { + let script = document.createElement('script'); + script.src = src; + script.async = false; + document.body.append(script); +} + +// async=false なので long.js が最初に実行されます +loadScript("/article/script-async-defer/long.js"); +loadScript("/article/script-async-defer/small.js"); +``` + +`script.async=false` がなければ、スクリプトはデフォルトであるロードファースト順(おそらく `small.js` が最初)で実行されます。 + +繰り返しますが、`defer` と同じように、ライブラリを読み込み、それに依存する別のスクリプトを読み込むような場合は、順序が重要になります。 + + +## サマリ + +`async` と `defer` には共通点が1つあります: これらのスクリプトのダウンロードはページのレンダリングをブロックしません。なので、ユーザはページコンテンツを見ることができ、すぎにそのページのことを知ることができます。 + +ですが、それらの間にも本質的な違いがあります: + +| | 順序 | `DOMContentLoaded` | +|---------|---------|---------| +| `async` | *ロードファースト順*. どちらが最初に読み込まれるか、ドキュメントでの順序は関係ありません | 無関係。ドキュメントがまだ完全にダウンロードされていないときにロードして実行する場合があります。これはスクリプトが小さいまたはキャッシュされていて、ドキュメントが十分に長い場合に発生します。| +| `defer` | *ドキュメント順* (ドキュメントに書かれている順). | ドキュメントがロードされ、パースされた後(必要に応じて待機します)、`DOMContentLoaded` の直前に実行されます。| + +実際には、`defer` は DOM全体が必要、かつ(または)それらの相対的な実行順序が重要なスクリプトに対して使用されます。 + +`async` はカウンターや広告など独立したスクリプトに対し使用されます。それらの相対的な実行順序は重要ではありません。 + +```warn header="スクリプトのないページが使用可能である必要があります" +注意してください: `defer` や `async` を使用している場合、ユーザはスクリプトがロードされる *前* にページを見ます。 + +このようなケースでは、グラフィカルなコンポーネントのいくつかはおそらくまだ初期化されていません。 + +"ロード中" のインジケータを表示し、まだ機能していないボタンを無効にするのを忘れないでください。ユーザがページで何ができるのか、そして何がまだ準備中であるかを明確に分かるようにする必要があります。 +``` diff --git a/2-ui/5-loading/02-script-async-defer/long.js b/2-ui/5-loading/02-script-async-defer/long.js new file mode 100644 index 0000000000..48710f1c62 --- /dev/null +++ b/2-ui/5-loading/02-script-async-defer/long.js @@ -0,0 +1,32 @@ +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... +// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js... + +alert("Long script loaded"); diff --git a/2-ui/5-loading/02-script-async-defer/small.js b/2-ui/5-loading/02-script-async-defer/small.js new file mode 100644 index 0000000000..ec743a025d --- /dev/null +++ b/2-ui/5-loading/02-script-async-defer/small.js @@ -0,0 +1,3 @@ +// ...small js... + +alert("Small script loaded"); diff --git a/2-ui/5-loading/02-script-async-defer/window-onbeforeunload.view/index.html b/2-ui/5-loading/02-script-async-defer/window-onbeforeunload.view/index.html new file mode 100644 index 0000000000..eacfda1f76 --- /dev/null +++ b/2-ui/5-loading/02-script-async-defer/window-onbeforeunload.view/index.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> +</head> +<body> + <script> + function setHandler() { + window.onbeforeunload = function() { + return "There are unsaved changes. Leave now?"; + }; + } + </script> + + <button onclick="setHandler()">Set window.onbeforeunload</button> + + <a href="http://example.com">Leave for EXAMPLE.COM</a> +</body> +</html> diff --git a/2-ui/3-event-details/11-onload-onerror/1-load-img-callback/solution.md b/2-ui/5-loading/03-onload-onerror/1-load-img-callback/solution.md similarity index 100% rename from 2-ui/3-event-details/11-onload-onerror/1-load-img-callback/solution.md rename to 2-ui/5-loading/03-onload-onerror/1-load-img-callback/solution.md diff --git a/2-ui/3-event-details/11-onload-onerror/1-load-img-callback/solution.view/index.html b/2-ui/5-loading/03-onload-onerror/1-load-img-callback/solution.view/index.html similarity index 100% rename from 2-ui/3-event-details/11-onload-onerror/1-load-img-callback/solution.view/index.html rename to 2-ui/5-loading/03-onload-onerror/1-load-img-callback/solution.view/index.html diff --git a/2-ui/3-event-details/11-onload-onerror/1-load-img-callback/source.view/index.html b/2-ui/5-loading/03-onload-onerror/1-load-img-callback/source.view/index.html similarity index 100% rename from 2-ui/3-event-details/11-onload-onerror/1-load-img-callback/source.view/index.html rename to 2-ui/5-loading/03-onload-onerror/1-load-img-callback/source.view/index.html diff --git a/2-ui/3-event-details/11-onload-onerror/1-load-img-callback/task.md b/2-ui/5-loading/03-onload-onerror/1-load-img-callback/task.md similarity index 100% rename from 2-ui/3-event-details/11-onload-onerror/1-load-img-callback/task.md rename to 2-ui/5-loading/03-onload-onerror/1-load-img-callback/task.md diff --git a/2-ui/5-loading/03-onload-onerror/article.md b/2-ui/5-loading/03-onload-onerror/article.md new file mode 100644 index 0000000000..79fe9a2c6d --- /dev/null +++ b/2-ui/5-loading/03-onload-onerror/article.md @@ -0,0 +1,89 @@ +# リソース読み込み: onload と onerror + +ブラウザは外部リソース -- スクリプト, iframes, 画像 など -- の読み込みを追跡することができます。 + +そのためのイベントが2つあります: + +- `onload` -- ロードが成功した, +- `onerror` -- エラーが発生した. + +## スクリプトの読み込み + +外部スクリプトにある関数を呼び出す必要があるとしましょう。 + +次のようにして動的にロードすることができます。: + +```js +let script = document.createElement('script'); +script.src = "my.js"; + +document.head.append(script); +``` + +...しかし、どうやってそのスクリプトの中で宣言された関数を実行するのでしょう?私たちはそのスクリプトの読み込みまで待つ必要があり、その後に初めて呼び出すことができます。 + +### script.onload + +主なヘルパーは `load` イベントです。スクリプトがロードされ、実行された後にトリガされます。 + +```js run untrusted +let script = document.createElement('script'); + +// 任意のドメインから任意のスクリプトがロードできます +script.src = "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js" +document.head.append(script); + +*!* +script.onload = function() { + // スクリプトはヘルパー関数 "_" を作ります + alert(_); // 関数は利用可能です +}; +*/!* +``` + +そのため、`onload` では、スクリプト変数の利用や関数の実行などが可能です。 + +...そして、仮に読み込みが失敗したらどうなるでしょう?例えば、そのようなスクリプトがない(404 エラー)もしくはサーバがない、サーバがダウンしている場合です。 + +### script.onerror + +スクリプトの読み込み(実行ではない)中に発生したエラーは `error` イベントで追跡することが可能です。 + +例えば、存在しないスクリプトを要求してみましょう: + +```js run +let script = document.createElement('script'); +script.src = "https://example.com/404.js"; // こんなスクリプトはありません +document.head.append(script); + +*!* +script.onerror = function() { + alert("Error loading " + this.src); // Error loading https://example.com/404.js +}; +*/!* +``` + +ここではエラーの詳細を取得することはできないことに注意してください。エラーが 404, 500, または他の何かだったのかは分かりません。単に読み込みに失敗したということだけです。 + +## 他のリソース + +`load` と `error` イベントは他のリソースに対しても機能します。そこには微妙な違いがあります。 + +例えば: + +`<img>`, `<link>` (外部のスタイルシート) +: `load` と `error` 両方のイベントは期待通りに機能します。 + +`<iframe>` +: iframe の読み込みが完了した時の `load` イベントのみです。ロードが成功した場合とエラーが発生した場合の両方をトリガーします。 これは歴史的な理由によるものです。 + +## サマリ + +画像 `<img>`, 外部スタイル, スクリプトや他のリソースは、それらの読み込みを追跡するために `load` と `error` イベントを提供しています。: + +- `load` はロードが成功したときにトリガされます。 +- `error` はロードに失敗したときにトリガされます。 + +唯一の例外は `<iframe>` です: 歴史的な理由により、どんな完了にもかかわらず(たとえページが見つからなくても)、常に `load` をトリガします。 + +`readystatechange` イベントもリソースに対して機能しますが、殆ど使われません。なぜなら `load/error` イベントの方がシンプルなためです。 \ No newline at end of file diff --git a/2-ui/5-loading/index.md b/2-ui/5-loading/index.md new file mode 100644 index 0000000000..bf9e816f62 --- /dev/null +++ b/2-ui/5-loading/index.md @@ -0,0 +1,2 @@ + +# ドキュメントとリソースの読み込み diff --git a/2-ui/99-ui-misc/01-mutation-observer/article.md b/2-ui/99-ui-misc/01-mutation-observer/article.md new file mode 100644 index 0000000000..e0015d6fc9 --- /dev/null +++ b/2-ui/99-ui-misc/01-mutation-observer/article.md @@ -0,0 +1,273 @@ + +# Mutation observer + +`MutationObserver` は DOM 要素を監視し、変更があった場合にコールバックを起動する組み込みのオブジェクトです。 + +最初に構文を確認してから、実際のユースケースを見ていきましょう。 + +## 構文 + +`MutationObserver` は簡単に使用できます。 + +まず、コールバック関数を引数にもつオブザーバ(observer)を作成します。: + +```js +let observer = new MutationObserver(callback); +``` + +次に、DOM ノードにアタッチします。: + +```js +observer.observe(node, config); +``` + +`config` はブール値のオプションを持つオブジェクトで、"どの種類の変更に反応するか" を指定します。: +- `childList` -- `node` の直接の子の変更, +- `subtree` -- `node` のすべての子孫に対する変更, +- `attributes` -- `node` の属性の変更, +- `attributeFilter` -- 指定したものだけを監視するための、属性の配列 +- `characterData` -- `node.data` (テキストコンテンツ) を監視するかどうか, + +ほかにもいくつかのオプションがあります: +- `attributeOldValue` -- `true` の場合、コールバックに属性の新旧両方の値を渡します。そうでなければ、新しいもののみを渡します(`attributes` オプションが必要です), +- `characterDataOldValue` -- `true` の場合、コールバックに `node.data` の新旧両方の値を渡します。そうでなければ、新しいもののみを渡します(`characterData` オプションが必要です), + +その後、任意の変更後に `callback` が実行されます。その際、最初の引数には [MutationRecord](https://dom.spec.whatwg.org/#mutationrecord) オブジェクトのリスト、2 番目の引数には、オブザーバ自体が与えられます。 + +[MutationRecord](https://dom.spec.whatwg.org/#mutationrecord) オブジェクトは次のプロパティを持っています: + +- `type` -- mutation(変化)の種類, 次のいずれかです。 + - `"attributes"` (属性が変更された) + - `"characterData"` (データが変更された) + - `"childList"` (要素が追加/削除された), +- `target` -- 変更が起こった場所: "attributes" の場合は要素, "characterData" の場合はテキストノード, あるいは "childList" の変化の場合は要素です, +- `addedNodes/removedNodes` -- 追加/削除されたノード, +- `previousSibling/nextSibling` -- 追加/削除されたノードの前後の兄弟, +- `attributeName/attributeNamespace` -- 変更された属性の名前/ネームスペース (XML の場合), +- `oldValue` -- 以前の値。属性あるいはテキストの変更の場合のみ(対応するオプション `attributeOldValue`/`characterDataOldValue` が設定されている場合) + +例えば、ここには `contentEditable` 属性をもつ `<div>` があります。この属性を指定すると、フォーカスすると編集することができます。 + +```html run +<div contentEditable id="elem">Click and <b>edit</b>, please</div> + +<script> +let observer = new MutationObserver(mutationRecords => { + console.log(mutationRecords); // console.log(the changes) +}); + +// 属性以外のすべてを監視する +observer.observe(elem, { + childList: true, // 直接の子を監視する + subtree: true, // 子孫たちも + characterDataOldValue: true // コールバックに古い値も渡す +}); +</script> +``` + +ブラウザで実行し、`<div>` にフォーカスし `<b>edit</b>` 内のテキストを変更すると、`console.log` に mutation(変化)が表示されます: + +```js +mutationRecords = [{ + type: "characterData", + oldValue: "edit", + target: <text node>, + // 他のプロパティは空です +}]; +``` + +より複雑な編集操作を行った場合、例えば `<b>edit</b>` を削除すると、mutation イベントは複数の mutation records を含みます: + +```js +mutationRecords = [{ + type: "childList", + target: <div#elem>, + removedNodes: [<b>], + nextSibling: <text node>, + previousSibling: <text node> + // 他のプロパティは空です +}, { + type: "characterData" + target: <text node> + // ...詳細は、ブラウザがどのように変更を処理するかによって異なります。 + // 2つの隣接するノード "Edit " と ", please" を1つのノードに合体するかもしれません。 + // あるいは、単に "Edit" の後の余分なスペースを削除する可能性もあります。 +}]; +``` + +したがって、`MutationObserver` で DOMサブツリー内の任意の変更に反応することができます。 + +## 統合するための使用法 + +いつ `MutationObserver` が必要でしょうか? + +便利な機能を含んでいるが、例えば広告 `<div class="ads">Unwanted ads</div>` を表示するといった好ましくないことも行う、3rdパーティのスクリプトを追加する必要があるケースを想像してください。 + +もちろん、3rd パーティのスクリプトには、広告を削除する仕組みは提供されていません。 + +`MutationObserver` を使用することで、好ましくない要素が DOM に表示された場合にそれを検知し、削除することができます。 + +別の状況としては、3rd パーティのスクリプトが我々のドキュメントに何かを追加した際に、それを検出し、動的にリサイズするなどページに適合させたい場合が考えられます。 + +`MutationObserver` を使用すると、これを実装することができます。 + +## アーキテクチャのための使用法 + +また、`MutationObserver` がアーキテクチャの観点から見て良い状況もあります。 + +例えば、プログラミングに関する Web サイトを作成しているとしましょう。当然ながら、記事やその他の資料にはソースコードのスニペットが含まれることがあります。 + +HTML マークアップでそのようなスニペットは次のようになります: + +```html +... +<pre class="language-javascript"><code> + // ここにコード + let hello = "world"; +</code></pre> +... +``` + +より読みやすくすると同時に、より美しくするために、[Prism.js](https://prismjs.com/) のような、JavaScript のシンタックスハイライトライブラリを使用します。Prism で上記のようなスニペットをシンタックスハイライトするには、`Prism.highlightElem(pre)` が呼ばれ、`pre` 要素の内容が検査され、ここのページの例と同じように、色付きの構文ハイライトが追加されます。 + +具体的にはいつハイライトを行うメソッドを実行すべきでしょうか?`DOMContentLoaded` イベント、あるいは、ページの末尾にスクリプトを配置することでハイライトができます。DOM が準備できた時点で、`pre[class*="language"]` を探し、それに対して `Prism.highlightElem` を呼び出すことが可能です: + +```js +// ページ上のすべてのコードスニペットをハイライト表示する +document.querySelectorAll('pre[class*="language"]').forEach(Prism.highlightElem); +``` + +今のところ、すべて簡単ですね。HTML 中に `<pre>` のコードスニペットがあり、それらをハイライト表示しています。 + +では続けましょう。サーバから資料や素材を動的に取得するとします。私たちは、[チュートリアルの後半](info:fetch-basics)でそのためのメソッドを学びます。ここでは、Web サーバから HTML の記事を取得し、オンデマンドでそれを表示する、という点がポイントです。: + +```js +let article = /* サーバから新しいコンテンツを取得する */ +articleElem.innerHTML = article; +``` + +新しい `article` HTML はコードスニペットを含むかもしれません。それらに対して `Prism.highlightElem` を呼び出す必要があります。そうしないと、ハイライト表示されません。 + +**動的にロードされた記事に対していつ、どこで `Prism.highlightElem` を呼び出すでしょうか?** + +次のように、記事をロードするコードへその呼び出しを追加することはできます。: + +```js +let article = /* サーバから新しいコンテンツを取得する */ +articleElem.innerHTML = article; + +*!* +let snippets = articleElem.querySelectorAll('pre[class*="language-"]'); +snippets.forEach(Prism.highlightElem); +*/!* +``` + +...しかし想像してみてください。コードでコンテンツをロードする場所はたくさんあります。: 記事、質問、フォーラムへの投稿など。到るところにハイライト表示をする呼び出しを書かなければならないのでしょうか?また、その呼び出しを忘れないように注意する必要もあります。 + +また、仮にコンテンツをサードパーティのエンジンにロードする場合はどうなるでしょう?E.g. 誰かによって書かれたフォーラムがあり、それは動的にコンテンツをロードします。そして、そこに構文のハイライト表示を追加したいような場合です。誰もサードパーティのスクリプトにパッチを当てるようなことはしたくありません。 + +幸いなことに、別の選択肢があります。 + +ページに挿入されたコードスニペットを自動で検出し、それらをハイライト表示するために、`MutationObserver` が使えます。 + +それにより、私たちはハイライト機能を一ヶ所で処理することができ、統合の必要性から解放されます。 + +## 動的なハイライト表示のデモ + +ここに動作サンプルがあります。 + +コードを実行すると、下の要素を監視し始め、そこに表れた任意のコードスニペットをハイライト表示します。: + +```js run +let observer = new MutationObserver(mutations => { + + for(let mutation of mutations) { + // 新しいノードを検査し、ハイライトするものはあるか? + + for(let node of mutation.addedNodes) { + // 要素のみを追跡し、他のノード(例 テキストノード)はスキップします + if (!(node instanceof HTMLElement)) continue; + + // 挿入された要素がコードスニペットであるかをチェックします + if (node.matches('pre[class*="language-"]')) { + Prism.highlightElement(node); + } + + // あるいは、サブツリーのどこかにコードスニペットがあるか? + for(let elem of node.querySelectorAll('pre[class*="language-"]')) { + Prism.highlightElement(elem); + } + } + } + +}); + +let demoElem = document.getElementById('highlight-demo'); + +observer.observe(demoElem, {childList: true, subtree: true}); +``` + +ここで、以下には HTML要素と、`innterHTML` を使用して動的にそれを埋めるJavaScript があります。 + +前のコード(上記、要素を監視します)、その後下のコードを実行してみてください。`MutationObserver` がどのようにスニペットを検出し、ハイライトするのかが確認できます。 + +<p id="highlight-demo" style="border: 1px solid #ddd"><code>id="highlight-demo"</code>を持つデモ要素です。上のサンプルで監視されています。</p> + +以下のコードは `innerHTML` でデータを設定します。もし上のコードを実行していれば、スニペットがハイライト表示されます。: + +```js run +let demoElem = document.getElementById('highlight-demo'); + +// コードスニペットを持つコンテンツを動的に挿入する +demoElem.innerHTML = `A code snippet is below: + <pre class="language-javascript"><code> let hello = "world!"; </code></pre> + <div>Another one:</div> + <div> + <pre class="language-css"><code>.class { margin: 5px; } </code></pre> + </div> +`; +``` + +これで、監視された要素または `document` 全体のすべてのハイライト表示を追跡できる `MutationObserver` ができました。それについて考えることなく、コードスニペットの追加/削除ができます。 + +## 追加のメソッド + +ノードの監視を停止するためのメソッドがあります: + +- `observer.disconnect()` -- 監視をやめます + +監視を停止するとき、オブザーバによりまだ処理されていない変更がある可能性があります。このような場合は以下を使用します + +- `observer.takeRecords()` -- 未処理の mutation(変化)のレコード一覧を取得します。これらは、変化は発生したが、コールバックはそれらを処理しなかったものです。 + +これらのメソッドは次のように一緒に使用されます: + +```js +// 未処理の mutation の一覧を取得します +// 処理されない可能性のある最近の変更を考慮する場合 +// disconnect の前に呼ぶ必要があります +let mutationRecords = observer.takeRecords(); + +// 変更の追跡をストップします +observer.disconnect(); +... +``` + + +```smart header="`observer.takeRecords()` で返却されたレコードは、処理キューから削除されます" +`observer.takeRecords()` で返却されたレコードに対するコールバックは呼ばれません。 +``` + +```smart header="ガベージコレクションの相互作用" +内部的に、オブザーバはノードに対して弱い参照を使用します。つまり、ノードがDOMから削除され、到達不可能になると、ガベージコレクションの対象となりえます。 + +DOM ノードが観測されたという事実だけでは、ガベージコレクションを防ぐことはできません。 +``` + +## サマリ + +`MutationObserver` は DOM の変更に反応することができます: 属性、要素の追加/削除、テキストコンテンツ。 + +これを使って、私たち自身の他の部分のコード、あるいは 3rd パーティのコードによってもたらされた変更を追跡することができます。 + +`MutationObserver` は任意の変更を追跡可能です。"何を監視するか" オプションの設定は最適化のために使用され、不要なコールバックの呼び出しにリソースを費やすことはありません。 diff --git a/2-ui/99-ui-misc/02-selection-range/article.md b/2-ui/99-ui-misc/02-selection-range/article.md new file mode 100644 index 0000000000..6c2c711199 --- /dev/null +++ b/2-ui/99-ui-misc/02-selection-range/article.md @@ -0,0 +1,716 @@ +libs: + - d3 + - domtree + +--- + +# 選択(Selection) と 範囲(Range) + +この章ではドキュメントでの選択と、`<input>` などのフォームフィールドでの選択について説明します。 + +JavaScript を利用して選択状態を取得したり、全体あるいは一部分の選択/選択解除、ドキュメントから選択した部分を削除、タグへのラップなどを行うことができます。 + +末尾の "サマリ" セクションにレシピがあり、これで現時点で必要なことはカバーされているかもしれません。ただ、章全体を読むことでより多くのことを知ることができます。 + +基礎となる `Range` と `Selection` オブジェクトの把握は難しくはないので、必要なことをするためのレシピは必要ありません。 + +## 範囲(Range) + +選択の基本的な概念は [範囲(Range)](https://dom.spec.whatwg.org/#ranges) で、基本的には "境界点"(範囲の開始と終了) のペアです。 + +`Range` オブジェクトはパラメータなしで作成できます: + +```js +let range = new Range(); +``` + +次に、`range.setStart(node, offset)` と `range.setEnd(node, offset)` を使用して選択の境界を設定します。 + +ご想像のとおり、さらに選択をしていくために `Range` オブジェクトを使用しますが、最初にそのようなオブジェクトをいくつか作成しましょう。 + +### テキストを部分的に選択 + +興味深いことは、両方のメソッドの最初の引数 `node` はテキストノード、あるいは要素ノードで、2つ目の引数の意味はその種類によります。 + +**`node` がテキストノードの場合、`offset` はそのテキストでの位置になります。** + +例えば、要素 `<p>Hello</p>` がある場合、次のようにして文字 "ll" を含む範囲を作成できます。: + +```html run +<p id="p">Hello</p> +<script> + let range = new Range(); + range.setStart(p.firstChild, 2); + range.setEnd(p.firstChild, 4); + + // range の toString はテキストとしてコンテンツを返します + console.log(range); // ll +</script> +``` + +ここでは `<p>` の最初の子(テキストノード)を取り、その中のテキスト位置を指定しています。: + +![](range-hello-1.svg) + +### 要素ノードの選択 + +**一方、`node` が要素ノードであれば、`offset` は子供の番号になります。** + +これは、テキスト内のどこかで停止するのではなく、ノード全体を含む範囲を作成する場合に便利です。 + +例として、この HTML の一部を考えます: + +```html +<p id="p">Example: <i>italic</i> and <b>bold</b></p> +``` + +DOM構造は次の通りです。ここではテキストノードが重要です。: + +<div class="select-p-domtree"></div> + +<script> +let selectPDomtree = { + "name": "P", + "nodeType": 1, + "children": [{ + "name": "#text", + "nodeType": 3, + "content": "Example: " + }, { + "name": "I", + "nodeType": 1, + "children": [{ + "name": "#text", + "nodeType": 3, + "content": "italic" + }] + }, { + "name": "#text", + "nodeType": 3, + "content": " and " + }, { + "name": "B", + "nodeType": 1, + "children": [{ + "name": "#text", + "nodeType": 3, + "content": "bold" + }] + }] +} + +drawHtmlTree(selectPDomtree, 'div.select-p-domtree', 690, 320); +</script> + +`"Example: <i>italic</i>"` の範囲を作成しましょう。 + +ご覧の通り、これはインデックス `0` と `1` を持つ `<p>` の先頭から2つの子です: + +![](range-example-p-0-1.svg) + +- 開始位置は親の `node` として `<p>` を、オフセットは `0` を持ちます。 + + なので、`range.setStart(p, 0)` と設定できます。 +- 終了位置も親ノードとして `<p>` を持ちますが、オフセットとしては `2` になります(ここまで、という範囲を指定しますが、`offset` の含みません)。 + + そのため、 `range.setEnd(p, 2)` と設定できます。 + +これはデモです。実行するとテキストが選択されるのが分かります: + +```html run +<p id="p">Example: <i>italic</i> and <b>bold</b></p> + +<script> +*!* + let range = new Range(); + + range.setStart(p, 0); + range.setEnd(p, 2); +*/!* + + // range の toString はそのコンテンツをテキストとして(タグなし)返します + alert(range); // Example: italic + + // この range をドキュメント選択に適用します(後で説明します) + document.getSelection().addRange(range); +</script> +``` + +これはより柔軟な例で多くのパターンを試せます。: + +```html run autorun +<p id="p">Example: <i>italic</i> and <b>bold</b></p> + +From <input id="start" type="number" value=1> – To <input id="end" type="number" value=4> +<button id="button">Click to select</button> +<script> + button.onclick = () => { + *!* + let range = new Range(); + + range.setStart(p, start.value); + range.setEnd(p, end.value); + */!* + + // apply the selection, explained later + document.getSelection().removeAllRanges(); + document.getSelection().addRange(range); + }; +</script> +``` + +例. `1` から `4` を選択した場合の範囲は `<i>italic</i> and <b>bold</b>` です。 + +![](range-example-p-1-3.svg) + +```smart header="開始ノードと終了ノードは異なっていても問題ありません" +`setStart` と `setEnd` で同じノードを指定する必要はありません。範囲(range)は多数の無関係なノードにまたがる場合があります。文書内で終了が開始の後にあることだけが重要です。 +``` + +### より大きなフラグメントの選択 + +次のように、例の中でより大きな選択を作成してみましょう: + +![](range-example-p-2-b-3.svg) + +すでにやり方は知っています。開始と終了をテキストノードで相対オフセットとしてセットするだけです。 + +次のような範囲を作成します: +- `<p>` の最初の子の位置 2 から開始("Ex<b>ample:</b> " の最初の2文字を除くすべて) +- `<b>` の最初の子の位置 3 で終了("<b>bol</b>d" の最初の3文字): + +```html run +<p id="p">Example: <i>italic</i> and <b>bold</b></p> + +<script> + let range = new Range(); + + range.setStart(p.firstChild, 2); + range.setEnd(p.querySelector('b').firstChild, 3); + + alert(range); // ample: italic and bol + + // 選択にこの範囲を使用します(後ほど説明します) + window.getSelection().addRange(range); +</script> +``` + +ご覧の通り、必要とする範囲(range)を作成するのは非常に簡単です。 + +もしノード全体をトリたい場合、`setStart/setEnd` で要素を渡すことができます。それ以外の場合はテキストレベルで作業できます。 + +## 範囲(range)プロパティ + +上の例で作成した範囲(range)オブジェクトは次のプロパティを持ちます: + +![](range-example-p-2-b-3-range.svg) + +- `startContainer`, `startOffset` -- 開始点のノードとオフセット + - 上の例では、`p` 内の最初のテキストノートと `2` です。 +- `endContainer`, `endOffset` -- 終了点のノードとオフセット + - 上の例では、`<b>` 内の最初のテキストノードと `3` です。 +- `collapsed` -- 真偽値, range の開始/終了点が同じ(つまり range 内にコンテンツがない)場合は `true` です。 + - 上の例では `false` です。 +- `commonAncestorContainer` -- range 内のすべてのノードの最も近い共通の祖先 + - 上の例では `<p>` です。 + + +## 範囲(range) 選択メソッド + +範囲を操作するための便利なメソッドがたくさんあります。 + +すでに `setStart` と `setEnd` は見てきましたが、ここには他の類似メソッドがあります。 + +範囲の開始を設定: + +- `setStart(node, offset)` は `node` 内の `offset` の位置に開始点を設定します。 +- `setStartBefore(node)` は `node` の直前を開始点に設定します。 +- `setStartAfter(node)` は `node` の直後を開始点に設定します。 + +範囲の終了を設定(同様のメソッドです): + +- `setEnd(node, offset)` は `node` 内の `offset` の位置に終了点を設定します。 +- `setEndBefore(node)` `node` の直前を終了点に設定します。 +- `setEndAfter(node)` は `node` の直後を終了点に設定します。 + +技術的には、`setStart/setEnd` は何でもできますが、より多くのメソッドでさらに便利になります。 + +これらのメソッドはすべて、`node` はテキストあるいは要素ノード両方になれます。テキストノードの場合 `offset` は文字をスキップする一方で、要素ノードは子ノードをスキップします。 + +範囲(range)を作成するためのメソッドが他にもあります: +- `selectNode(node)` は `node` 全体を選択するような範囲を設定します。 +- `selectNodeContents(node)` は `node` のコンテンツ全体を選択するような範囲を設定します +- `collapse(toStart)` は、`toStart=true` の場合 end=start 、そうでなければ start=end を設定します。範囲を折りたたみます。 +- `cloneRange()` は同じ開始/終了点をもつ新しい範囲を作成します。 + +## 範囲(range)編集メソッド + +一度範囲(range)を作成し、次のようなメソッドを使用することでコンテンツを操作することができます: + +- `deleteContents()` - ドキュメントから範囲のコンテンツを削除します +- `extractContents()` - ドキュメントから範囲のコンテンツを削除し、[DocumentFragment](info:modifying-document#document-fragment) として返却します。 +- `cloneContents()` - 範囲のコンテンツをクローンし、[DocumentFragment](info:modifying-document#document-fragment) として返却します。 +- `insertNode(node)` -- ドキュメントの範囲の先頭に `node` を挿入します。 +- `surroundContents(node)` -- `node` で範囲コンテンツをラップします。これが機能するには、範囲内にすべての要素の開始と終了タグが含まれている必要があります。`<i>abc` のような部分的な範囲では機能しません。 + +これらのメソッドを使用すると、選択したノードに対し基本的に何でもできます。 + +これは実際の動作が確認できる例です。: + +```html run autorun height=260 +ボタンクリックで選択範囲に対しメソッドを実行し、"resetExample" でリセットします。 + +<p id="p">Example: <i>italic</i> and <b>bold</b></p> + +<p id="result"></p> +<script> + let range = new Range(); + + // Each demonstrated method is represented here: + let methods = { + deleteContents() { + range.deleteContents() + }, + extractContents() { + let content = range.extractContents(); + result.innerHTML = ""; + result.append("extracted: ", content); + }, + cloneContents() { + let content = range.cloneContents(); + result.innerHTML = ""; + result.append("cloned: ", content); + }, + insertNode() { + let newNode = document.createElement('u'); + newNode.innerHTML = "NEW NODE"; + range.insertNode(newNode); + }, + surroundContents() { + let newNode = document.createElement('u'); + try { + range.surroundContents(newNode); + } catch(e) { alert(e) } + }, + resetExample() { + p.innerHTML = `Example: <i>italic</i> and <b>bold</b>`; + result.innerHTML = ""; + + range.setStart(p.firstChild, 2); + range.setEnd(p.querySelector('b').firstChild, 3); + + window.getSelection().removeAllRanges(); + window.getSelection().addRange(range); + } + }; + + for(let method in methods) { + document.write(`<div><button onclick="methods.${method}()">${method}</button></div>`); + } + + methods.resetExample(); +</script> +``` + +範囲を比較するメソッドも存在しますが、めったに使われることはありません。必要になったら、[仕様](https://dom.spec.whatwg.org/#interface-range) や [MDN マニュアル](https://developer.mozilla.org/en-US/docs/Web/API/Range)を参照してください。 + + +## 選択(Selection) + +`Range` は選択範囲を管理するための汎用オブジェクトです。このようなオブジェクトを作成し利用しますが-- それらは自身では視覚的には何も選択しません。 + +ドキュメントの選択は `Selection` オブジェクトで表現され、`window.getSelection()` あるいは `document.getSelection()` で取得することができます。 + +選択は 0 個以上の範囲を含めることができます。すくなくとも [Selection API 仕様](https://www.w3.org/TR/selection-api/) ではそのように述べています。ですが、実際には Firefox のみが `key:Ctrl+click` (Mac の場合は `key:Cmd+click`)を使用してドキュメント内の複数の範囲を選択することができます。 + +これは Firefox で 3つの範囲を選択しているスクリーンショットです。: + +![](selection-firefox.svg) + +他のブラウザは最大1つの範囲だけサポートしています。後で見ていきますが、`Selection` メソッドによっては複数の範囲が存在する可能性があることを示唆しているものもあります。が、繰り返しになりますが Firefox 以外のブラウザは最大で 1 です。 + +これは小さなデモで、現在の選択(何かを選択してクリック)をテキストとして表示します。 + +<button onclick="alert(document.getSelection())">alert(document.getSelection())</button> + +## Selection プロパティ + +前述したように、理論上、選択には複数の範囲が含まれる場合があります。これらの範囲オブジェクトは、次のメソッドを使用して取得できます。 + +- `getRangeAt(i)` -- `0` から始まる i 番目の範囲を取得します。Firefox を除くすべてのブラウザでは、`0` のみが使用されます。 + +また、多くの場合、利便性が向上するプロパティも存在します。 + +範囲(range)と同様、選択には始点と終点があり、それぞれ "anchor(アンカー)"、"focus(フォーカス)" と呼ばれます。 + +主な selection プロパティは次のものです: + +- `anchorNode` -- selection の始点のある node です。 +- `anchorOffset` -- selection の始点の `anchorNode` でのオフセットです。 +- `focusNode` -- selection の終点のある node です。 +- `focusOffset` -- selection の終点の `focusNode` でのオフセットです。 +- `isCollapsed` -- selection が未選択(空の範囲) あるいは存在しない場合 `true` になります。 +- `rangeCount` -- selection に含まれる range の数です。 + +````smart header="選択(selection) 終了/開始 vs 範囲(range)" + +選択(selection)の アンカー/フォーカス は `Range` の 開始/終了 と比較して重要な違いがあります。 + +ご存知の通り、`Range` オブジェクトは常に 終了の前に開始があります。 + +選択の場合、常にそうであるわけではありません。 + +マウスで何かを選択することは、「左から右」または「右から左」の両方向で行うことができます。 + +つまり、マウス ボタンを押してドキュメント内を前に移動すると、その終了点 (フォーカス) は開始点 (アンカー) の後に来ます。 + +E.g. ユーザがマウスで選択を開始し、"Example" から "italic" まで操作した場合: + +![](selection-direction-forward.svg) + +...しかし、同じ選択を逆方向に行うこともできます。"italic" から "Example" まで開始すると (逆方向)、その終了 (フォーカス) が開始 (アンカー) の前になります。 + +![](selection-direction-backward.svg) +```` + +## Selection イベント + +選択範囲を追跡するためのイベントがあります: + +- `elem.onselectstart` -- `elem`(またはその内部)で選択が *開始* されたとき。例えば、ユーザがボタンを押しながらマウスを動かし始めたとき。 + - デフォルトアクションを防いだ場合、選択は開始されません。したがって、この要素から選択を開始することは不可能になりますが、要素は引き続き選択可能です。訪問者は他の場所から選択を開始するだけで済みます。 +- `document.onselectionchange` -- 選択範囲が変更されたとき。 + - 注意: このハンドラは `document` に対してのみ設定可能です。 + +### 選択範囲の追跡デモ + +これは選択境界の変更に応じて動的に選択境界を表示する小さなデモです: + +```html run height=80 +<p id="p">Select me: <i>italic</i> and <b>bold</b></p> + +From <input id="from" disabled> – To <input id="to" disabled> +<script> + document.onselectionchange = function() { + let selection = document.getSelection(); + + let {anchorNode, anchorOffset, focusNode, focusOffset} = selection; + + // anchorNode と focusNode は通常テキストノードです + from.value = `${anchorNode?.data}, offset ${anchorOffset}`; + to.value = `${focusNode?.data}, offset ${focusOffset}`; + }; +</script> +``` + +### 選択(selection)のコピーデモ + +選択されたコンテンツをコピーする、2つのアプローチがあります: + +1. `document.getSelection().toString()` を使用してテキストとして取得できます。 +2. DOM ノードとして: 基底となる範囲を取得し、それらの `cloneContents()` を呼び出します(Firefox のマルチ選択をサポートしてない場合は最初の1つの range に対してのみ)。 + +そして、これはテキストとDOM ノード両方で選択範囲を取得するデモです: + +```html run height=100 +<p id="p">Select me: <i>italic</i> and <b>bold</b></p> + +Cloned: <span id="cloned"></span> +<br> +As text: <span id="astext"></span> + +<script> + document.onselectionchange = function() { + let selection = document.getSelection(); + + cloned.innerHTML = astext.innerHTML = ""; + + // range から DOM ノードをクローンします(ここでは multiselect をサポートしています) + for (let i = 0; i < selection.rangeCount; i++) { + cloned.append(selection.getRangeAt(i).cloneContents()); + } + + // テキストとして取得 + astext.innerHTML += selection; + }; +</script> +``` + +## Selection メソッド + +range の追加/削除をするための Selection メソッド: + +- `getRangeAt(i)` -- `0` から始まる i 番目の range を取得します。firefox 以外のブラウザは `0` だけが使用されます。 +- `addRange(range)` -- 選択範囲に `range` を追加します。すでに range が関連付けられている場合、firefox 以外のブラウザは呼び出しを無視します。 +- `removeRange(range)` -- selection から `range` を削除します。 +- `removeAllRanges()` -- すべての range を削除します。 +- `empty()` -- `removeAllRanges` のエイリアスです。 + +また、`Range` なしで選択範囲をを直接操作するための便利なメソッドがあります: + +- `collapse(node, offset)` -- 選択された range を、指定された `node` の位置 `offset` で開始及び終了する新しい range に置き換えます。 +- `setPosition(node, offset)` -- `collapse` のエイリアスです。 +- `collapseToStart()` - 選択範囲の始点に折りたたみます(空の range に置き換えます) +- `collapseToEnd()` - 選択範囲の終点に折りたたみます +- `extend(node, offset)` - 選択範囲の focus を指定された `node` の位置 `offset` に移動します。 +- `setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset)` - 選択範囲の range を、指定された始点 `anchorNode/anchorOffset` と終点 `focusNode/focusOffset` に置き換えます。これらの間にあるすべてのコンテンツが選択されます。 +- `selectAllChildren(node)` -- `node` のすべての子を選択します。 +- `deleteFromDocument()` -- ドキュメントから選択されたコンテンツを削除します。 +- `containsNode(node, allowPartialContainment = false)` -- 選択範囲が `node` を含むかチェックします(2番めの引数が `true` の場合は部分的に含む、を許可する)。 + +したがって、多くのタスクで `Selection` メソッドを呼び出すことができ、基礎となる `Range` オブジェクトにアクセスする必要はありません。 + +例えば、段落 `<p>` のコンテンツ全体を選択するには次のようにします: + +```html run +<p id="p">Select me: <i>italic</i> and <b>bold</b></p> + +<script> + // <p> の 0 番目の子から最後の子までを選択 + document.getSelection().setBaseAndExtent(p, 0, p, p.childNodes.length); +</script> +``` + +The same thing using ranges: + +```html run +<p id="p">Select me: <i>italic</i> and <b>bold</b></p> + +<script> + let range = new Range(); + range.selectNodeContents(p); // or selectNode(p) で <p> タグも選択します + + document.getSelection().removeAllRanges(); // 存在する選択範囲をクリアします + document.getSelection().addRange(range); +</script> +``` + +```smart header="選択するには、最初に既存の選択範囲を削除してください" +選択範囲がすでに存在する場合、`removeAllRanges()` で最初に空にし、その後 range を追加してください。そうでない場合、Firefox 以外のブラウザは新しい range を無視します。 + +`setBaseAndExtent` などのいくつかの selection メソッドは例外で、既存の選択範囲を置き換えます。 +``` + +## フォームコントロールでの選択 + +`input` や `textarea` と行ったフォーム要素は `Selection` や `Range` オブジェクトなしで、[選択のための特別なAPI](https://html.spec.whatwg.org/#textFieldSelection) を提供します。入力値は HTML ではなく純粋なテキストなので、このようなオブジェクトは必要なく、すべてがよりシンプルです。 + +プロパティ: +- `input.selectionStart` -- 選択開始位置(書き込み可能) +- `input.selectionEnd` -- 選択終了位置(書き込み可能) +- `input.selectionDirection` -- 選択方向。次のいずれかです: "forward", "backward" あるいは "none" (例 マウスのダブルクリックで選択した場合 ) です。 + +イベント: +- `input.onselect` -- 何かが選択されたときに発生します。 + +メソッド: + +- `input.select()` -- テキストコントロール内のすべてを選択します(`input` の代わりに `textarea` も OK です)。 +- `input.setSelectionRange(start, end, [direction])` -- 選択範囲を、`start` 位置から `end` 位置まで、指定された方向(任意)に広げます。 +- `input.setRangeText(replacement, [start], [end], [selectionMode])` -- テキストの範囲を新しいテキストに置き換えます。 + + 任意の引数 `start` と `end` が指定された場合は開始/終了の範囲を設定します。そうでない場合はユーザの選択を使用されます。 + + 最後の引数 `selectionMode` はテキストが置換されたあとの選択方法を決定します。次の値が指定できます: + + - `"select"` -- 新たに挿入されたテキストが選択されます。 + - `"start"` -- 選択範囲は挿入されたテキストの直前に折りたたまれます(カーソルはその直前になります)。 + - `"end"` -- 選択範囲は挿入されたテキストの直後に折りたたまれます(カーソルはその直後になります)。 + - `"preserve"` -- 選択範囲を保とうとします。デフォルトはこの動作になります。 + +それでは、これらのメソッドの動作を見てみましょう。 + +### 例: 選択の追跡 + +例えば、このコードは `onselect` イベントを使用して選択を追跡します: + +```html run autorun +<textarea id="area" style="width:80%;height:60px"> +Selecting in this text updates values below. +</textarea> +<br> +From <input id="from" disabled> – To <input id="to" disabled> + +<script> + area.onselect = function() { + from.value = area.selectionStart; + to.value = area.selectionEnd; + }; +</script> +``` + +注意してください: +- `onselect` はなにかが選択されたときに発生しますが、選択が除去されたときには発生しません。 +- `document.onselectionchange` イベントは、[仕様](https://w3c.github.io/selection-api/#dfn-selectionchange)によると `document` の選択や範囲とは関係ないため、フォームコントロール内の選択では発生すべきではありません。一部のブラウザにはイベントを生成しますが、それに頼るべきではありません。 + + +### 例: カーソルの移動 + +選択範囲を設定する `selectionStart` と `selectionEnd` を変更することができます。 + +重要なエッジケースは `selectionStart` と `selectionEnd` が互いに等しい場合です。そのとき、それはまさにカーソル位置になります。あるいは何も選択されていない場合は、選択はカーソル位置で折りたたまれます。 + +したがって、`selectionStart` と `selectionEnd` を同じ値に設定することでカーソルを移動させることができます。 + +例: + +```html run autorun +<textarea id="area" style="width:80%;height:60px"> +Focus on me, the cursor will be at position 10. +</textarea> + +<script> + area.onfocus = () => { + // ブラウザの "focus" アクションが終了したあとに実行させるためのゼロ遅延の setTimeout + setTimeout(() => { + // 任意の選択を設定できます + // 開始 = 終了 の場合、カーソルはまさにその場所です + area.selectionStart = area.selectionEnd = 10; + }); + }; +</script> +``` + +### 例: 選択の変更 + +選択内容を変更するには、`input.setRangeText()` メソッドが利用できます。もちろん、`selectionStart/End` を読み取り、選択に関する知識があれば `value` の対応する部分文字列を変更することはできますが、`setRangeText` はより強力で、多くの場合より便利です。 + +これはいくらか複雑なメソッドです。最もシンプルな単一引数のフォームでは、ユーザの選択した範囲を置き換え、選択を削除します。 + +例えば、ここではユーザの選択は `*...*` で囲まれます: + +```html run autorun +<input id="input" style="width:200px" value="Select here and click the button"> +<button id="button">Wrap selection in stars *...*</button> + +<script> +button.onclick = () => { + if (input.selectionStart == input.selectionEnd) { + return; // 何も選択されていない + } + + let selected = input.value.slice(input.selectionStart, input.selectionEnd); + input.setRangeText(`*${selected}*`); +}; +</script> +``` + +引数を増やすことで、範囲の `start` と `end` を設定することができます。 + +この例では、入力テキストから `"THIS"` を見つけ、それを置き換え、置換された選択範囲を維持します: + +```html run autorun +<input id="input" style="width:200px" value="Replace THIS in text"> +<button id="button">Replace THIS</button> + +<script> +button.onclick = () => { + let pos = input.value.indexOf("THIS"); + if (pos >= 0) { + input.setRangeText("*THIS*", pos, pos + 4, "select"); + input.focus(); // 選択を見えるようにするための focus + } +}; +</script> +``` + +### 例: カーソル位置に挿入 + +何も選択されていない場合、あるいは `setRangeText` で `start` と `end` を等しくした場合、新しいテキストが単に挿入されるだけで何も削除はされません。 + +`setRangeText` を使用して "カーソル位置" に挿入することもできます。 + +これはカーソル位置に `"HELLO"` を挿入し、その直後にカーソルを置くボタンです。選択が空でない場合は置き換えられます(`selectionStart!=selectionEnd` を比較することでこれを検知し、代わりになにかを行うことができます): + +```html run autorun +<input id="input" style="width:200px" value="Text Text Text Text Text"> +<button id="button">Insert "HELLO" at cursor</button> + +<script> + button.onclick = () => { + input.setRangeText("HELLO", input.selectionStart, input.selectionEnd, "end"); + input.focus(); + }; +</script> +``` + + +## 選択不可にする + +選択不可にするための3つの方法があります: + +1. CSS プロパティ `user-select: none` を使用します. + + ```html run + <style> + #elem { + user-select: none; + } + </style> + <div>Selectable <div id="elem">Unselectable</div> Selectable</div> + ``` + + これは `elem` から始まる選択を許可しません。ですが、ユーザは別の場所から選択を開始して、その中に `elem` を含むかもしれません。 + + すると、`elem` は`document.getSelection()` の一部になるので、選択は実際には起こります。が、その中身は通常コピー/貼り付けでは無視されます。 + + +2. `onselectstart` または `mousedown` イベントで、デフォルト動作を防止します。 + + ```html run + <div>Selectable <div id="elem">Unselectable</div> Selectable</div> + + <script> + elem.onselectstart = () => false; + </script> + ``` + + これにより `elem` から選択を開始するのことはできなくなりますが、訪問者は別の要素で選択を開始して `elem` まで広げることができます。 + + これは、選択をトリガーする同じアクションに別のイベントハンドラーがある場合に便利です(例. `mousedown`)。そのため、競合を避けるために選択を無効にしつつ、依然として `elem` の内容はコピーできるようにします。 + +3. `document.getSelection().empty()` が発生したあと、事後の選択をクリアすることもできます。これは選択が表示されて消えるときに不要な点滅を引き起こすため、めったに使われません。 + +## リファレンス + +- [DOM 仕様: Range](https://dom.spec.whatwg.org/#ranges) +- [Selection API](https://www.w3.org/TR/selection-api/#dom-globaleventhandlers-onselectstart) +- [HTML 仕様: テキストコントロール選択用 API](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#textFieldSelection) + + +## サマリ + +選択のための2つの異なる API について説明しました: + +1. document に対して: `Selection` と `Range` オブジェクト +2. `input`, `textarea` に対して: 追加のメソッドとプロパティ + +2つ目のAPIはテキストに対してのみ動作するのでとてもシンプルです。 + +最もよく使用されるレシピはおそらく次のものです: + +1. 選択を取得する: + ```js run + let selection = document.getSelection(); + + let cloned = /* 選択したノードのクローンを作成する要素 */; + + // 次に selection.getRangeAt(0) に対して Range メソッドを適用します。 + // あるいは、ここのように複数選択をサポートするためにすべての範囲に適用します。 + for (let i = 0; i < selection.rangeCount; i++) { + cloned.append(selection.getRangeAt(i).cloneContents()); + } + ``` +2. 選択を設定する: + ```js run + let selection = document.getSelection(); + + // 直接: + selection.setBaseAndExtent(...from...to...); + + // あるいは range を作成し設定します: + selection.removeAllRanges(); + selection.addRange(range); + ``` + +そして、最後にカーソルについてです。`<textarea>` のような編集可能な要素のカーソル位置は常に選択範囲の先頭あるいは末尾になります。これを使用して、`elem.selectionStart` や `elem.selectionEnd` を設定することでカーソル位置を取得したりカーソルを移動させることができます。 \ No newline at end of file diff --git a/2-ui/99-ui-misc/02-selection-range/range-example-p-0-1.svg b/2-ui/99-ui-misc/02-selection-range/range-example-p-0-1.svg new file mode 100644 index 0000000000..9ebcffaac4 --- /dev/null +++ b/2-ui/99-ui-misc/02-selection-range/range-example-p-0-1.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="640" height="89" viewBox="0 0 640 89"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><path id="path-1" d="M47 35h262v21H47z"/><mask id="mask-2" width="262" height="21" x="0" y="0" fill="#fff" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox"><use xlink:href="#path-1"/></mask></defs><g id="selection-range" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="range-example-p-0-1.svg"><use id="Rectangle" fill="#C9DCEA" stroke="#C9DCEA" stroke-dasharray="5,2" stroke-width="2" mask="url(#mask-2)" xlink:href="#path-1"/><g id="Group" fill="#AF6E24" fill-rule="nonzero" transform="translate(13 35)"><path id="<p>Example:<i>italic</i>and<b>bold</b></p>" d="M.81 10.51v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08L.81 10.51zm12.718-.36a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM37.326 3.4h7.398v1.332H38.82v4.122h5.454v1.332H38.82v4.482h5.994V16h-7.488V3.4zm14.428 7.992L48.28 7h1.8l2.592 3.384L55.336 7h1.62l-3.474 4.32 3.69 4.68h-1.728l-2.862-3.69L49.666 16h-1.638l3.726-4.608zm9.19-3.69a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM75.516 16v-5.994a9.51 9.51 0 00-.027-.729 2.442 2.442 0 00-.126-.63 1.01 1.01 0 00-.288-.441.734.734 0 00-.495-.162c-.408 0-.753.168-1.035.504-.282.336-.495.756-.639 1.26V16h-1.368V7h.936l.27 1.098h.072c.12-.18.237-.351.351-.513.114-.162.246-.303.396-.423.15-.12.327-.213.531-.279.204-.066.462-.099.774-.099.18 0 .366.027.558.081.192.054.369.138.531.252.162.114.303.267.423.459s.198.426.234.702c.276-.468.585-.834.927-1.098.342-.264.813-.396 1.413-.396.396 0 .717.066.963.198s.438.321.576.567c.138.246.234.54.288.882.054.342.081.723.081 1.143V16h-1.368V9.88c0-.252-.012-.489-.036-.711a2.268 2.268 0 00-.135-.585.967.967 0 00-.27-.396c-.114-.096-.267-.144-.459-.144-.42 0-.774.168-1.062.504-.288.336-.504.816-.648 1.44V16h-1.368zm8.812-5.85a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zM96.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm19.18 11.466a4.53 4.53 0 01-.765.54 5.645 5.645 0 01-.963.432 7.06 7.06 0 01-1.089.279 6.633 6.633 0 01-1.143.099c-.72 0-1.356-.111-1.908-.333a3.674 3.674 0 01-1.386-.954 4.158 4.158 0 01-.846-1.485c-.192-.576-.288-1.224-.288-1.944 0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.504 0 1.002.066 1.494.198a2.95 2.95 0 011.305.747c.378.366.669.87.873 1.512.204.642.276 1.473.216 2.493h-6.966c0 1.08.291 1.887.873 2.421.582.534 1.359.801 2.331.801.324 0 .645-.039.963-.117.318-.078.621-.171.909-.279.288-.108.543-.228.765-.36.222-.132.393-.252.513-.36l.558 1.026zm-3.798-6.894a4.86 4.86 0 00-1.125.126 2.564 2.564 0 00-.936.423c-.27.198-.492.456-.666.774-.174.318-.285.711-.333 1.179h5.598c-.06-.792-.315-1.407-.765-1.845-.45-.438-1.041-.657-1.773-.657zm10.27-.036c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm0 7.074c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm20.216-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zM155.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM178.89 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zM189.358 7h2.142V5.218l1.404-.396V7h4.806v1.206h-4.806v4.248c0 .876.213 1.524.639 1.944.426.42 1.035.63 1.827.63.54 0 1.011-.102 1.413-.306a7.614 7.614 0 001.089-.666l.468 1.062c-.42.336-.921.606-1.503.81a5.377 5.377 0 01-1.791.306c-.48 0-.933-.069-1.359-.207a3.12 3.12 0 01-1.125-.639 3.06 3.06 0 01-.774-1.107c-.192-.45-.288-.987-.288-1.611V8.206h-2.142V7zm13.186.702a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM214.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zM226.09 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm15.166 3.942a7.111 7.111 0 00-.891-.243 4.64 4.64 0 00-.945-.099c-1.176 0-2.052.276-2.628.828-.576.552-.864 1.428-.864 2.628 0 .528.084.999.252 1.413.168.414.408.765.72 1.053.312.288.687.51 1.125.666.438.156.921.234 1.449.234.564 0 1.113-.096 1.647-.288.534-.192.981-.444 1.341-.756l.63 1.044a5.095 5.095 0 01-.63.45 6.053 6.053 0 01-1.971.774 6.38 6.38 0 01-1.305.126c-.78 0-1.467-.111-2.061-.333a3.914 3.914 0 01-1.485-.954 4.083 4.083 0 01-.9-1.494 5.818 5.818 0 01-.306-1.935c0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.888 0 1.611.078 2.169.234.558.156 1.029.33 1.413.522l-.018.054v2.502h-1.296v-1.71zm4.708 2.124v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zM273.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm24.05-7.704a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zm8.884-4.806a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.412l.162 1.458h.09c.12-.204.279-.405.477-.603a3.741 3.741 0 011.575-.927 3.593 3.593 0 011.026-.144c.552 0 1.041.06 1.467.18.426.12.78.333 1.062.639.282.306.495.72.639 1.242.144.522.216 1.185.216 1.989V16h-1.404v-4.896c0-.996-.162-1.746-.486-2.25-.324-.504-.912-.756-1.764-.756-.312 0-.615.063-.909.189a3.137 3.137 0 00-.801.495c-.24.204-.45.441-.63.711-.18.27-.312.555-.396.855V16h-1.386v-5.85zm16.408-6.75h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm19.208-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm11.026-7.11h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM389.436 3.4h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.192 3.51c0-1.452.378-2.601 1.134-3.447.756-.846 1.836-1.269 3.24-1.269.756 0 1.407.123 1.953.369s.999.579 1.359.999c.36.42.627.918.801 1.494.174.576.261 1.194.261 1.854 0 .72-.096 1.371-.288 1.953a4.057 4.057 0 01-.846 1.485 3.738 3.738 0 01-1.377.945c-.546.222-1.167.333-1.863.333-.744 0-1.392-.123-1.944-.369a3.815 3.815 0 01-1.368-.999 4.073 4.073 0 01-.801-1.494 6.381 6.381 0 01-.261-1.854zm1.494 0c0 .42.051.84.153 1.26.102.42.267.798.495 1.134.228.336.525.606.891.81.366.204.813.306 1.341.306.96 0 1.683-.297 2.169-.891.486-.594.729-1.467.729-2.619 0-.432-.051-.855-.153-1.269a3.353 3.353 0 00-.504-1.125 2.656 2.656 0 00-.9-.81c-.366-.204-.813-.306-1.341-.306-.96 0-1.68.294-2.16.882-.48.588-.72 1.464-.72 2.628zm11.422-8.1h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm15.94 0h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm7.408-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm3.718.216h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm11.116-4.896v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm5.41 6.966a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896z"/></g><path id="Path-Copy" stroke="#C06334" stroke-width="2" d="M48 57v11h102V57"/><path id="Path-Copy-2" stroke="#C06334" stroke-width="2" d="M154 57v11h154V57"/><text id="0" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="93" y="84">0</tspan></text><text id="1" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="226" y="84">1</tspan></text><text id="2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="334" y="84">2</tspan></text><text id="3" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="428" y="84">3</tspan></text><path id="Path-Copy-3" stroke="#C06334" stroke-width="2" d="M312 57v11h53V57"/><path id="Path-Copy-4" stroke="#C06334" stroke-width="2" d="M369 57v11h128V57"/></g></g></svg> \ No newline at end of file diff --git a/2-ui/99-ui-misc/02-selection-range/range-example-p-1-3.svg b/2-ui/99-ui-misc/02-selection-range/range-example-p-1-3.svg new file mode 100644 index 0000000000..088c71c208 --- /dev/null +++ b/2-ui/99-ui-misc/02-selection-range/range-example-p-1-3.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="640" height="89" viewBox="0 0 640 89"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><path id="path-1" d="M153 35h345v21H153z"/><mask id="mask-2" width="345" height="21" x="0" y="0" fill="#fff" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox"><use xlink:href="#path-1"/></mask></defs><g id="selection-range" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="range-example-p-1-3.svg"><use id="Rectangle" fill="#C9DCEA" stroke="#C9DCEA" stroke-dasharray="5,2" stroke-width="2" mask="url(#mask-2)" xlink:href="#path-1"/><path id="Path-Copy" stroke="#C06334" stroke-width="2" d="M48 57v11h102V57"/><path id="Path-Copy-2" stroke="#C06334" stroke-width="2" d="M154 57v11h154V57"/><text id="0" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="93" y="84">0</tspan></text><text id="1" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="226" y="84">1</tspan></text><text id="2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="334" y="84">2</tspan></text><text id="3" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="428" y="84">3</tspan></text><path id="Path-Copy-3" stroke="#C06334" stroke-width="2" d="M312 57v11h53V57"/><path id="Path-Copy-4" stroke="#C06334" stroke-width="2" d="M369 57v11h128V57"/><g id="Group" fill="#AF6E24" fill-rule="nonzero" transform="translate(13 35)"><path id="<p>Example:<i>italic</i>and<b>bold</b></p>" d="M.81 10.51v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08L.81 10.51zm12.718-.36a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM37.326 3.4h7.398v1.332H38.82v4.122h5.454v1.332H38.82v4.482h5.994V16h-7.488V3.4zm14.428 7.992L48.28 7h1.8l2.592 3.384L55.336 7h1.62l-3.474 4.32 3.69 4.68h-1.728l-2.862-3.69L49.666 16h-1.638l3.726-4.608zm9.19-3.69a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM75.516 16v-5.994a9.51 9.51 0 00-.027-.729 2.442 2.442 0 00-.126-.63 1.01 1.01 0 00-.288-.441.734.734 0 00-.495-.162c-.408 0-.753.168-1.035.504-.282.336-.495.756-.639 1.26V16h-1.368V7h.936l.27 1.098h.072c.12-.18.237-.351.351-.513.114-.162.246-.303.396-.423.15-.12.327-.213.531-.279.204-.066.462-.099.774-.099.18 0 .366.027.558.081.192.054.369.138.531.252.162.114.303.267.423.459s.198.426.234.702c.276-.468.585-.834.927-1.098.342-.264.813-.396 1.413-.396.396 0 .717.066.963.198s.438.321.576.567c.138.246.234.54.288.882.054.342.081.723.081 1.143V16h-1.368V9.88c0-.252-.012-.489-.036-.711a2.268 2.268 0 00-.135-.585.967.967 0 00-.27-.396c-.114-.096-.267-.144-.459-.144-.42 0-.774.168-1.062.504-.288.336-.504.816-.648 1.44V16h-1.368zm8.812-5.85a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zM96.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm19.18 11.466a4.53 4.53 0 01-.765.54 5.645 5.645 0 01-.963.432 7.06 7.06 0 01-1.089.279 6.633 6.633 0 01-1.143.099c-.72 0-1.356-.111-1.908-.333a3.674 3.674 0 01-1.386-.954 4.158 4.158 0 01-.846-1.485c-.192-.576-.288-1.224-.288-1.944 0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.504 0 1.002.066 1.494.198a2.95 2.95 0 011.305.747c.378.366.669.87.873 1.512.204.642.276 1.473.216 2.493h-6.966c0 1.08.291 1.887.873 2.421.582.534 1.359.801 2.331.801.324 0 .645-.039.963-.117.318-.078.621-.171.909-.279.288-.108.543-.228.765-.36.222-.132.393-.252.513-.36l.558 1.026zm-3.798-6.894a4.86 4.86 0 00-1.125.126 2.564 2.564 0 00-.936.423c-.27.198-.492.456-.666.774-.174.318-.285.711-.333 1.179h5.598c-.06-.792-.315-1.407-.765-1.845-.45-.438-1.041-.657-1.773-.657zm10.27-.036c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm0 7.074c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm20.216-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zM155.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM178.89 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zM189.358 7h2.142V5.218l1.404-.396V7h4.806v1.206h-4.806v4.248c0 .876.213 1.524.639 1.944.426.42 1.035.63 1.827.63.54 0 1.011-.102 1.413-.306a7.614 7.614 0 001.089-.666l.468 1.062c-.42.336-.921.606-1.503.81a5.377 5.377 0 01-1.791.306c-.48 0-.933-.069-1.359-.207a3.12 3.12 0 01-1.125-.639 3.06 3.06 0 01-.774-1.107c-.192-.45-.288-.987-.288-1.611V8.206h-2.142V7zm13.186.702a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM214.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zM226.09 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm15.166 3.942a7.111 7.111 0 00-.891-.243 4.64 4.64 0 00-.945-.099c-1.176 0-2.052.276-2.628.828-.576.552-.864 1.428-.864 2.628 0 .528.084.999.252 1.413.168.414.408.765.72 1.053.312.288.687.51 1.125.666.438.156.921.234 1.449.234.564 0 1.113-.096 1.647-.288.534-.192.981-.444 1.341-.756l.63 1.044a5.095 5.095 0 01-.63.45 6.053 6.053 0 01-1.971.774 6.38 6.38 0 01-1.305.126c-.78 0-1.467-.111-2.061-.333a3.914 3.914 0 01-1.485-.954 4.083 4.083 0 01-.9-1.494 5.818 5.818 0 01-.306-1.935c0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.888 0 1.611.078 2.169.234.558.156 1.029.33 1.413.522l-.018.054v2.502h-1.296v-1.71zm4.708 2.124v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zM273.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm24.05-7.704a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zm8.884-4.806a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.412l.162 1.458h.09c.12-.204.279-.405.477-.603a3.741 3.741 0 011.575-.927 3.593 3.593 0 011.026-.144c.552 0 1.041.06 1.467.18.426.12.78.333 1.062.639.282.306.495.72.639 1.242.144.522.216 1.185.216 1.989V16h-1.404v-4.896c0-.996-.162-1.746-.486-2.25-.324-.504-.912-.756-1.764-.756-.312 0-.615.063-.909.189a3.137 3.137 0 00-.801.495c-.24.204-.45.441-.63.711-.18.27-.312.555-.396.855V16h-1.386v-5.85zm16.408-6.75h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm19.208-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm11.026-7.11h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM389.436 3.4h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.192 3.51c0-1.452.378-2.601 1.134-3.447.756-.846 1.836-1.269 3.24-1.269.756 0 1.407.123 1.953.369s.999.579 1.359.999c.36.42.627.918.801 1.494.174.576.261 1.194.261 1.854 0 .72-.096 1.371-.288 1.953a4.057 4.057 0 01-.846 1.485 3.738 3.738 0 01-1.377.945c-.546.222-1.167.333-1.863.333-.744 0-1.392-.123-1.944-.369a3.815 3.815 0 01-1.368-.999 4.073 4.073 0 01-.801-1.494 6.381 6.381 0 01-.261-1.854zm1.494 0c0 .42.051.84.153 1.26.102.42.267.798.495 1.134.228.336.525.606.891.81.366.204.813.306 1.341.306.96 0 1.683-.297 2.169-.891.486-.594.729-1.467.729-2.619 0-.432-.051-.855-.153-1.269a3.353 3.353 0 00-.504-1.125 2.656 2.656 0 00-.9-.81c-.366-.204-.813-.306-1.341-.306-.96 0-1.68.294-2.16.882-.48.588-.72 1.464-.72 2.628zm11.422-8.1h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm15.94 0h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm7.408-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm3.718.216h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm11.116-4.896v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm5.41 6.966a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896z"/></g></g></g></svg> \ No newline at end of file diff --git a/2-ui/99-ui-misc/02-selection-range/range-example-p-2-b-3-range.svg b/2-ui/99-ui-misc/02-selection-range/range-example-p-2-b-3-range.svg new file mode 100644 index 0000000000..f13c6d74a7 --- /dev/null +++ b/2-ui/99-ui-misc/02-selection-range/range-example-p-2-b-3-range.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="683" height="229" viewBox="0 0 683 229"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><path id="path-1" d="M183 54h367v21H183z"/><mask id="mask-2" width="367" height="21" x="0" y="0" fill="#fff" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox"><use xlink:href="#path-1"/></mask></defs><g id="selection-range" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="range-example-p-2-b-3-range.svg"><path id="Path" stroke="#C06334" stroke-width="2" d="M160 68v10h106V68"/><path id="Path-Copy-2" stroke="#C06334" stroke-width="2" d="M126 143v19h532v-19"/><path id="Path-Copy" stroke="#C06334" stroke-width="2" d="M514 70v10h50V70"/><use id="Rectangle" fill="#C9DCEA" stroke="#C9DCEA" stroke-dasharray="5,2" stroke-width="2" mask="url(#mask-2)" xlink:href="#path-1"/><text id="startContainer-(<p>." fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="10.3" y="118">startContainer</tspan> <tspan x=".7" y="136">(<p>.firstChild)</tspan></text><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M183 74l7 14h-6v16h-2V88h-6l7-14zM155 81l-6.166 14.387-3.876-4.581-15.312 12.957-.763.646-1.292-1.526.763-.646 15.312-12.958-3.875-4.58L155 81zM501 82l-6.166 14.387-3.876-4.581-15.312 12.957-.763.646-1.292-1.526.763-.646 15.312-12.958-3.875-4.58L501 82z"/><path id="Line-Copy-2" fill="#C06334" fill-rule="nonzero" d="M550 73l7 14h-6v16h-2V87h-6l7-14z"/><text id="startOffset-(=2)" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="170.2" y="118">startOffset</tspan> <tspan x="203.8" y="136">(=2)</tspan></text><text id="commonAncestorContai" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="316.1" y="181">commonAncestorContainer</tspan> <tspan x="402.5" y="199">(<p>)</tspan></text><text id="endContainer-(<b>.fi" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="388.9" y="117">endContainer</tspan> <tspan x="369.7" y="135">(<b>.firstChild)</tspan></text><text id="endOffset-(=3)" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="537.3" y="118">endOffset</tspan> <tspan x="561.3" y="136">(=3)</tspan></text><g id="Group" fill="#AF6E24" fill-rule="nonzero" transform="translate(125 54)"><path id="<p>Example:<i>italic</i>and<b>bold</b></p>" d="M.81 10.51v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08L.81 10.51zm12.718-.36a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM37.326 3.4h7.398v1.332H38.82v4.122h5.454v1.332H38.82v4.482h5.994V16h-7.488V3.4zm14.428 7.992L48.28 7h1.8l2.592 3.384L55.336 7h1.62l-3.474 4.32 3.69 4.68h-1.728l-2.862-3.69L49.666 16h-1.638l3.726-4.608zm9.19-3.69a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM75.516 16v-5.994a9.51 9.51 0 00-.027-.729 2.442 2.442 0 00-.126-.63 1.01 1.01 0 00-.288-.441.734.734 0 00-.495-.162c-.408 0-.753.168-1.035.504-.282.336-.495.756-.639 1.26V16h-1.368V7h.936l.27 1.098h.072c.12-.18.237-.351.351-.513.114-.162.246-.303.396-.423.15-.12.327-.213.531-.279.204-.066.462-.099.774-.099.18 0 .366.027.558.081.192.054.369.138.531.252.162.114.303.267.423.459s.198.426.234.702c.276-.468.585-.834.927-1.098.342-.264.813-.396 1.413-.396.396 0 .717.066.963.198s.438.321.576.567c.138.246.234.54.288.882.054.342.081.723.081 1.143V16h-1.368V9.88c0-.252-.012-.489-.036-.711a2.268 2.268 0 00-.135-.585.967.967 0 00-.27-.396c-.114-.096-.267-.144-.459-.144-.42 0-.774.168-1.062.504-.288.336-.504.816-.648 1.44V16h-1.368zm8.812-5.85a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zM96.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm19.18 11.466a4.53 4.53 0 01-.765.54 5.645 5.645 0 01-.963.432 7.06 7.06 0 01-1.089.279 6.633 6.633 0 01-1.143.099c-.72 0-1.356-.111-1.908-.333a3.674 3.674 0 01-1.386-.954 4.158 4.158 0 01-.846-1.485c-.192-.576-.288-1.224-.288-1.944 0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.504 0 1.002.066 1.494.198a2.95 2.95 0 011.305.747c.378.366.669.87.873 1.512.204.642.276 1.473.216 2.493h-6.966c0 1.08.291 1.887.873 2.421.582.534 1.359.801 2.331.801.324 0 .645-.039.963-.117.318-.078.621-.171.909-.279.288-.108.543-.228.765-.36.222-.132.393-.252.513-.36l.558 1.026zm-3.798-6.894a4.86 4.86 0 00-1.125.126 2.564 2.564 0 00-.936.423c-.27.198-.492.456-.666.774-.174.318-.285.711-.333 1.179h5.598c-.06-.792-.315-1.407-.765-1.845-.45-.438-1.041-.657-1.773-.657zm10.27-.036c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm0 7.074c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm20.216-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zM155.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM178.89 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zM189.358 7h2.142V5.218l1.404-.396V7h4.806v1.206h-4.806v4.248c0 .876.213 1.524.639 1.944.426.42 1.035.63 1.827.63.54 0 1.011-.102 1.413-.306a7.614 7.614 0 001.089-.666l.468 1.062c-.42.336-.921.606-1.503.81a5.377 5.377 0 01-1.791.306c-.48 0-.933-.069-1.359-.207a3.12 3.12 0 01-1.125-.639 3.06 3.06 0 01-.774-1.107c-.192-.45-.288-.987-.288-1.611V8.206h-2.142V7zm13.186.702a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM214.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zM226.09 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm15.166 3.942a7.111 7.111 0 00-.891-.243 4.64 4.64 0 00-.945-.099c-1.176 0-2.052.276-2.628.828-.576.552-.864 1.428-.864 2.628 0 .528.084.999.252 1.413.168.414.408.765.72 1.053.312.288.687.51 1.125.666.438.156.921.234 1.449.234.564 0 1.113-.096 1.647-.288.534-.192.981-.444 1.341-.756l.63 1.044a5.095 5.095 0 01-.63.45 6.053 6.053 0 01-1.971.774 6.38 6.38 0 01-1.305.126c-.78 0-1.467-.111-2.061-.333a3.914 3.914 0 01-1.485-.954 4.083 4.083 0 01-.9-1.494 5.818 5.818 0 01-.306-1.935c0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.888 0 1.611.078 2.169.234.558.156 1.029.33 1.413.522l-.018.054v2.502h-1.296v-1.71zm4.708 2.124v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zM273.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm24.05-7.704a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zm8.884-4.806a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.412l.162 1.458h.09c.12-.204.279-.405.477-.603a3.741 3.741 0 011.575-.927 3.593 3.593 0 011.026-.144c.552 0 1.041.06 1.467.18.426.12.78.333 1.062.639.282.306.495.72.639 1.242.144.522.216 1.185.216 1.989V16h-1.404v-4.896c0-.996-.162-1.746-.486-2.25-.324-.504-.912-.756-1.764-.756-.312 0-.615.063-.909.189a3.137 3.137 0 00-.801.495c-.24.204-.45.441-.63.711-.18.27-.312.555-.396.855V16h-1.386v-5.85zm16.408-6.75h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm19.208-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm11.026-7.11h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM389.436 3.4h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.192 3.51c0-1.452.378-2.601 1.134-3.447.756-.846 1.836-1.269 3.24-1.269.756 0 1.407.123 1.953.369s.999.579 1.359.999c.36.42.627.918.801 1.494.174.576.261 1.194.261 1.854 0 .72-.096 1.371-.288 1.953a4.057 4.057 0 01-.846 1.485 3.738 3.738 0 01-1.377.945c-.546.222-1.167.333-1.863.333-.744 0-1.392-.123-1.944-.369a3.815 3.815 0 01-1.368-.999 4.073 4.073 0 01-.801-1.494 6.381 6.381 0 01-.261-1.854zm1.494 0c0 .42.051.84.153 1.26.102.42.267.798.495 1.134.228.336.525.606.891.81.366.204.813.306 1.341.306.96 0 1.683-.297 2.169-.891.486-.594.729-1.467.729-2.619 0-.432-.051-.855-.153-1.269a3.353 3.353 0 00-.504-1.125 2.656 2.656 0 00-.9-.81c-.366-.204-.813-.306-1.341-.306-.96 0-1.68.294-2.16.882-.48.588-.72 1.464-.72 2.628zm11.422-8.1h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm15.94 0h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm7.408-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm3.718.216h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm11.116-4.896v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm5.41 6.966a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896z"/></g></g></g></svg> \ No newline at end of file diff --git a/2-ui/99-ui-misc/02-selection-range/range-example-p-2-b-3.svg b/2-ui/99-ui-misc/02-selection-range/range-example-p-2-b-3.svg new file mode 100644 index 0000000000..4bf5b00b00 --- /dev/null +++ b/2-ui/99-ui-misc/02-selection-range/range-example-p-2-b-3.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="640" height="89" viewBox="0 0 640 89"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><path id="path-1" d="M71 35h367v21H71z"/><mask id="mask-2" width="367" height="21" x="0" y="0" fill="#fff" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox"><use xlink:href="#path-1"/></mask></defs><g id="selection-range" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="range-example-p-2-b-3.svg"><use id="Rectangle" fill="#C9DCEA" stroke="#C9DCEA" stroke-dasharray="5,2" stroke-width="2" mask="url(#mask-2)" xlink:href="#path-1"/><path id="Path-Copy" stroke="#C06334" stroke-width="2" d="M48 57v11h102V57"/><path id="Path-Copy-2" stroke="#C06334" stroke-width="2" d="M154 57v11h154V57"/><text id="0" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="93" y="84">0</tspan></text><text id="1" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="226" y="84">1</tspan></text><text id="2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="334" y="84">2</tspan></text><text id="3" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="428" y="84">3</tspan></text><path id="Path-Copy-3" stroke="#C06334" stroke-width="2" d="M312 57v11h53V57"/><path id="Path-Copy-4" stroke="#C06334" stroke-width="2" d="M369 57v11h128V57"/><g id="Group" fill="#AF6E24" fill-rule="nonzero" transform="translate(13 35)"><path id="<p>Example:<i>italic</i>and<b>bold</b></p>" d="M.81 10.51v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08L.81 10.51zm12.718-.36a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM37.326 3.4h7.398v1.332H38.82v4.122h5.454v1.332H38.82v4.482h5.994V16h-7.488V3.4zm14.428 7.992L48.28 7h1.8l2.592 3.384L55.336 7h1.62l-3.474 4.32 3.69 4.68h-1.728l-2.862-3.69L49.666 16h-1.638l3.726-4.608zm9.19-3.69a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM75.516 16v-5.994a9.51 9.51 0 00-.027-.729 2.442 2.442 0 00-.126-.63 1.01 1.01 0 00-.288-.441.734.734 0 00-.495-.162c-.408 0-.753.168-1.035.504-.282.336-.495.756-.639 1.26V16h-1.368V7h.936l.27 1.098h.072c.12-.18.237-.351.351-.513.114-.162.246-.303.396-.423.15-.12.327-.213.531-.279.204-.066.462-.099.774-.099.18 0 .366.027.558.081.192.054.369.138.531.252.162.114.303.267.423.459s.198.426.234.702c.276-.468.585-.834.927-1.098.342-.264.813-.396 1.413-.396.396 0 .717.066.963.198s.438.321.576.567c.138.246.234.54.288.882.054.342.081.723.081 1.143V16h-1.368V9.88c0-.252-.012-.489-.036-.711a2.268 2.268 0 00-.135-.585.967.967 0 00-.27-.396c-.114-.096-.267-.144-.459-.144-.42 0-.774.168-1.062.504-.288.336-.504.816-.648 1.44V16h-1.368zm8.812-5.85a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zM96.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm19.18 11.466a4.53 4.53 0 01-.765.54 5.645 5.645 0 01-.963.432 7.06 7.06 0 01-1.089.279 6.633 6.633 0 01-1.143.099c-.72 0-1.356-.111-1.908-.333a3.674 3.674 0 01-1.386-.954 4.158 4.158 0 01-.846-1.485c-.192-.576-.288-1.224-.288-1.944 0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.504 0 1.002.066 1.494.198a2.95 2.95 0 011.305.747c.378.366.669.87.873 1.512.204.642.276 1.473.216 2.493h-6.966c0 1.08.291 1.887.873 2.421.582.534 1.359.801 2.331.801.324 0 .645-.039.963-.117.318-.078.621-.171.909-.279.288-.108.543-.228.765-.36.222-.132.393-.252.513-.36l.558 1.026zm-3.798-6.894a4.86 4.86 0 00-1.125.126 2.564 2.564 0 00-.936.423c-.27.198-.492.456-.666.774-.174.318-.285.711-.333 1.179h5.598c-.06-.792-.315-1.407-.765-1.845-.45-.438-1.041-.657-1.773-.657zm10.27-.036c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm0 7.074c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm20.216-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zM155.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM178.89 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zM189.358 7h2.142V5.218l1.404-.396V7h4.806v1.206h-4.806v4.248c0 .876.213 1.524.639 1.944.426.42 1.035.63 1.827.63.54 0 1.011-.102 1.413-.306a7.614 7.614 0 001.089-.666l.468 1.062c-.42.336-.921.606-1.503.81a5.377 5.377 0 01-1.791.306c-.48 0-.933-.069-1.359-.207a3.12 3.12 0 01-1.125-.639 3.06 3.06 0 01-.774-1.107c-.192-.45-.288-.987-.288-1.611V8.206h-2.142V7zm13.186.702a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM214.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zM226.09 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm15.166 3.942a7.111 7.111 0 00-.891-.243 4.64 4.64 0 00-.945-.099c-1.176 0-2.052.276-2.628.828-.576.552-.864 1.428-.864 2.628 0 .528.084.999.252 1.413.168.414.408.765.72 1.053.312.288.687.51 1.125.666.438.156.921.234 1.449.234.564 0 1.113-.096 1.647-.288.534-.192.981-.444 1.341-.756l.63 1.044a5.095 5.095 0 01-.63.45 6.053 6.053 0 01-1.971.774 6.38 6.38 0 01-1.305.126c-.78 0-1.467-.111-2.061-.333a3.914 3.914 0 01-1.485-.954 4.083 4.083 0 01-.9-1.494 5.818 5.818 0 01-.306-1.935c0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.888 0 1.611.078 2.169.234.558.156 1.029.33 1.413.522l-.018.054v2.502h-1.296v-1.71zm4.708 2.124v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zM273.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm24.05-7.704a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zm8.884-4.806a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.412l.162 1.458h.09c.12-.204.279-.405.477-.603a3.741 3.741 0 011.575-.927 3.593 3.593 0 011.026-.144c.552 0 1.041.06 1.467.18.426.12.78.333 1.062.639.282.306.495.72.639 1.242.144.522.216 1.185.216 1.989V16h-1.404v-4.896c0-.996-.162-1.746-.486-2.25-.324-.504-.912-.756-1.764-.756-.312 0-.615.063-.909.189a3.137 3.137 0 00-.801.495c-.24.204-.45.441-.63.711-.18.27-.312.555-.396.855V16h-1.386v-5.85zm16.408-6.75h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm19.208-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm11.026-7.11h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM389.436 3.4h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.192 3.51c0-1.452.378-2.601 1.134-3.447.756-.846 1.836-1.269 3.24-1.269.756 0 1.407.123 1.953.369s.999.579 1.359.999c.36.42.627.918.801 1.494.174.576.261 1.194.261 1.854 0 .72-.096 1.371-.288 1.953a4.057 4.057 0 01-.846 1.485 3.738 3.738 0 01-1.377.945c-.546.222-1.167.333-1.863.333-.744 0-1.392-.123-1.944-.369a3.815 3.815 0 01-1.368-.999 4.073 4.073 0 01-.801-1.494 6.381 6.381 0 01-.261-1.854zm1.494 0c0 .42.051.84.153 1.26.102.42.267.798.495 1.134.228.336.525.606.891.81.366.204.813.306 1.341.306.96 0 1.683-.297 2.169-.891.486-.594.729-1.467.729-2.619 0-.432-.051-.855-.153-1.269a3.353 3.353 0 00-.504-1.125 2.656 2.656 0 00-.9-.81c-.366-.204-.813-.306-1.341-.306-.96 0-1.68.294-2.16.882-.48.588-.72 1.464-.72 2.628zm11.422-8.1h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm15.94 0h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm7.408-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm3.718.216h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm11.116-4.896v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm5.41 6.966a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896z"/></g></g></g></svg> \ No newline at end of file diff --git a/2-ui/99-ui-misc/02-selection-range/range-hello-1.svg b/2-ui/99-ui-misc/02-selection-range/range-hello-1.svg new file mode 100644 index 0000000000..2951607a2d --- /dev/null +++ b/2-ui/99-ui-misc/02-selection-range/range-hello-1.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="147" height="80" viewBox="0 0 147 80"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><path id="path-1" d="M63 17h25v21H63z"/><mask id="mask-2" width="25" height="21" x="0" y="0" fill="#fff" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox"><use xlink:href="#path-1"/></mask></defs><g id="selection-range" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="range-hello-1.svg"><use id="Rectangle" fill="#C9DCEA" stroke="#C9DCEA" stroke-dasharray="5,2" stroke-width="2" mask="url(#mask-2)" xlink:href="#path-1"/><text id="<p>Hello</p>" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="18" font-weight="normal" letter-spacing="1"><tspan x="5" y="33"><p>Hello</p></tspan></text><path id="Path-Copy" stroke="#C06334" stroke-width="2" d="M41 38v11h58.887V37"/><text id="p.firstChild" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="25" y="69">p.firstChild</tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/99-ui-misc/02-selection-range/selection-direction-backward.svg b/2-ui/99-ui-misc/02-selection-range/selection-direction-backward.svg new file mode 100644 index 0000000000..6399f9d5ee --- /dev/null +++ b/2-ui/99-ui-misc/02-selection-range/selection-direction-backward.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="559" height="94" viewBox="0 0 559 94"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><path id="path-1" d="M47 36h262v21H47z"/><mask id="mask-2" width="262" height="21" x="0" y="0" fill="#fff" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox"><use xlink:href="#path-1"/></mask></defs><g id="selection-range" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="selection-direction-backward.svg"><use id="Rectangle" fill="#C9DCEA" stroke="#C9DCEA" stroke-dasharray="5,2" stroke-width="2" mask="url(#mask-2)" xlink:href="#path-1"/><text id="focus" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="25" y="80">focus</tspan></text><text id="anchor" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="277.2" y="80">anchor</tspan></text><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M62 21v6h247v2H62v6l-14-7 14-7z"/><g id="Group" fill="#AF6E24" fill-rule="nonzero" transform="translate(13 37)"><path id="<p>Example:<i>italic</i>and<b>bold</b></p>" d="M.81 10.51v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08L.81 10.51zm12.718-.36a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM37.326 3.4h7.398v1.332H38.82v4.122h5.454v1.332H38.82v4.482h5.994V16h-7.488V3.4zm14.428 7.992L48.28 7h1.8l2.592 3.384L55.336 7h1.62l-3.474 4.32 3.69 4.68h-1.728l-2.862-3.69L49.666 16h-1.638l3.726-4.608zm9.19-3.69a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM75.516 16v-5.994a9.51 9.51 0 00-.027-.729 2.442 2.442 0 00-.126-.63 1.01 1.01 0 00-.288-.441.734.734 0 00-.495-.162c-.408 0-.753.168-1.035.504-.282.336-.495.756-.639 1.26V16h-1.368V7h.936l.27 1.098h.072c.12-.18.237-.351.351-.513.114-.162.246-.303.396-.423.15-.12.327-.213.531-.279.204-.066.462-.099.774-.099.18 0 .366.027.558.081.192.054.369.138.531.252.162.114.303.267.423.459s.198.426.234.702c.276-.468.585-.834.927-1.098.342-.264.813-.396 1.413-.396.396 0 .717.066.963.198s.438.321.576.567c.138.246.234.54.288.882.054.342.081.723.081 1.143V16h-1.368V9.88c0-.252-.012-.489-.036-.711a2.268 2.268 0 00-.135-.585.967.967 0 00-.27-.396c-.114-.096-.267-.144-.459-.144-.42 0-.774.168-1.062.504-.288.336-.504.816-.648 1.44V16h-1.368zm8.812-5.85a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zM96.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm19.18 11.466a4.53 4.53 0 01-.765.54 5.645 5.645 0 01-.963.432 7.06 7.06 0 01-1.089.279 6.633 6.633 0 01-1.143.099c-.72 0-1.356-.111-1.908-.333a3.674 3.674 0 01-1.386-.954 4.158 4.158 0 01-.846-1.485c-.192-.576-.288-1.224-.288-1.944 0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.504 0 1.002.066 1.494.198a2.95 2.95 0 011.305.747c.378.366.669.87.873 1.512.204.642.276 1.473.216 2.493h-6.966c0 1.08.291 1.887.873 2.421.582.534 1.359.801 2.331.801.324 0 .645-.039.963-.117.318-.078.621-.171.909-.279.288-.108.543-.228.765-.36.222-.132.393-.252.513-.36l.558 1.026zm-3.798-6.894a4.86 4.86 0 00-1.125.126 2.564 2.564 0 00-.936.423c-.27.198-.492.456-.666.774-.174.318-.285.711-.333 1.179h5.598c-.06-.792-.315-1.407-.765-1.845-.45-.438-1.041-.657-1.773-.657zm10.27-.036c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm0 7.074c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm20.216-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zM155.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM178.89 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zM189.358 7h2.142V5.218l1.404-.396V7h4.806v1.206h-4.806v4.248c0 .876.213 1.524.639 1.944.426.42 1.035.63 1.827.63.54 0 1.011-.102 1.413-.306a7.614 7.614 0 001.089-.666l.468 1.062c-.42.336-.921.606-1.503.81a5.377 5.377 0 01-1.791.306c-.48 0-.933-.069-1.359-.207a3.12 3.12 0 01-1.125-.639 3.06 3.06 0 01-.774-1.107c-.192-.45-.288-.987-.288-1.611V8.206h-2.142V7zm13.186.702a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM214.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zM226.09 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm15.166 3.942a7.111 7.111 0 00-.891-.243 4.64 4.64 0 00-.945-.099c-1.176 0-2.052.276-2.628.828-.576.552-.864 1.428-.864 2.628 0 .528.084.999.252 1.413.168.414.408.765.72 1.053.312.288.687.51 1.125.666.438.156.921.234 1.449.234.564 0 1.113-.096 1.647-.288.534-.192.981-.444 1.341-.756l.63 1.044a5.095 5.095 0 01-.63.45 6.053 6.053 0 01-1.971.774 6.38 6.38 0 01-1.305.126c-.78 0-1.467-.111-2.061-.333a3.914 3.914 0 01-1.485-.954 4.083 4.083 0 01-.9-1.494 5.818 5.818 0 01-.306-1.935c0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.888 0 1.611.078 2.169.234.558.156 1.029.33 1.413.522l-.018.054v2.502h-1.296v-1.71zm4.708 2.124v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zM273.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm24.05-7.704a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zm8.884-4.806a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.412l.162 1.458h.09c.12-.204.279-.405.477-.603a3.741 3.741 0 011.575-.927 3.593 3.593 0 011.026-.144c.552 0 1.041.06 1.467.18.426.12.78.333 1.062.639.282.306.495.72.639 1.242.144.522.216 1.185.216 1.989V16h-1.404v-4.896c0-.996-.162-1.746-.486-2.25-.324-.504-.912-.756-1.764-.756-.312 0-.615.063-.909.189a3.137 3.137 0 00-.801.495c-.24.204-.45.441-.63.711-.18.27-.312.555-.396.855V16h-1.386v-5.85zm16.408-6.75h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm19.208-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm11.026-7.11h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM389.436 3.4h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.192 3.51c0-1.452.378-2.601 1.134-3.447.756-.846 1.836-1.269 3.24-1.269.756 0 1.407.123 1.953.369s.999.579 1.359.999c.36.42.627.918.801 1.494.174.576.261 1.194.261 1.854 0 .72-.096 1.371-.288 1.953a4.057 4.057 0 01-.846 1.485 3.738 3.738 0 01-1.377.945c-.546.222-1.167.333-1.863.333-.744 0-1.392-.123-1.944-.369a3.815 3.815 0 01-1.368-.999 4.073 4.073 0 01-.801-1.494 6.381 6.381 0 01-.261-1.854zm1.494 0c0 .42.051.84.153 1.26.102.42.267.798.495 1.134.228.336.525.606.891.81.366.204.813.306 1.341.306.96 0 1.683-.297 2.169-.891.486-.594.729-1.467.729-2.619 0-.432-.051-.855-.153-1.269a3.353 3.353 0 00-.504-1.125 2.656 2.656 0 00-.9-.81c-.366-.204-.813-.306-1.341-.306-.96 0-1.68.294-2.16.882-.48.588-.72 1.464-.72 2.628zm11.422-8.1h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm15.94 0h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm7.408-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm3.718.216h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm11.116-4.896v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm5.41 6.966a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896z"/></g><text id="mouse-move-direction" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="93" y="22">mouse move direction</tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/99-ui-misc/02-selection-range/selection-direction-forward.svg b/2-ui/99-ui-misc/02-selection-range/selection-direction-forward.svg new file mode 100644 index 0000000000..03c6fc5c61 --- /dev/null +++ b/2-ui/99-ui-misc/02-selection-range/selection-direction-forward.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="563" height="94" viewBox="0 0 563 94"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><path id="path-1" d="M47 38h262v21H47z"/><mask id="mask-2" width="262" height="21" x="0" y="0" fill="#fff" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox"><use xlink:href="#path-1"/></mask></defs><g id="selection-range" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="selection-direction-forward.svg"><text id="anchor" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="18.2" y="78">anchor</tspan></text><text id="focus" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="285" y="78">focus</tspan></text><use id="Rectangle" fill="#C9DCEA" stroke="#C9DCEA" stroke-dasharray="5,2" stroke-width="2" mask="url(#mask-2)" xlink:href="#path-1"/><g id="Group" fill="#AF6E24" fill-rule="nonzero" transform="translate(13 39)"><path id="<p>Example:<i>italic</i>and<b>bold</b></p>" d="M.81 10.51v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08L.81 10.51zm12.718-.36a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM37.326 3.4h7.398v1.332H38.82v4.122h5.454v1.332H38.82v4.482h5.994V16h-7.488V3.4zm14.428 7.992L48.28 7h1.8l2.592 3.384L55.336 7h1.62l-3.474 4.32 3.69 4.68h-1.728l-2.862-3.69L49.666 16h-1.638l3.726-4.608zm9.19-3.69a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM75.516 16v-5.994a9.51 9.51 0 00-.027-.729 2.442 2.442 0 00-.126-.63 1.01 1.01 0 00-.288-.441.734.734 0 00-.495-.162c-.408 0-.753.168-1.035.504-.282.336-.495.756-.639 1.26V16h-1.368V7h.936l.27 1.098h.072c.12-.18.237-.351.351-.513.114-.162.246-.303.396-.423.15-.12.327-.213.531-.279.204-.066.462-.099.774-.099.18 0 .366.027.558.081.192.054.369.138.531.252.162.114.303.267.423.459s.198.426.234.702c.276-.468.585-.834.927-1.098.342-.264.813-.396 1.413-.396.396 0 .717.066.963.198s.438.321.576.567c.138.246.234.54.288.882.054.342.081.723.081 1.143V16h-1.368V9.88c0-.252-.012-.489-.036-.711a2.268 2.268 0 00-.135-.585.967.967 0 00-.27-.396c-.114-.096-.267-.144-.459-.144-.42 0-.774.168-1.062.504-.288.336-.504.816-.648 1.44V16h-1.368zm8.812-5.85a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zM96.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm19.18 11.466a4.53 4.53 0 01-.765.54 5.645 5.645 0 01-.963.432 7.06 7.06 0 01-1.089.279 6.633 6.633 0 01-1.143.099c-.72 0-1.356-.111-1.908-.333a3.674 3.674 0 01-1.386-.954 4.158 4.158 0 01-.846-1.485c-.192-.576-.288-1.224-.288-1.944 0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.504 0 1.002.066 1.494.198a2.95 2.95 0 011.305.747c.378.366.669.87.873 1.512.204.642.276 1.473.216 2.493h-6.966c0 1.08.291 1.887.873 2.421.582.534 1.359.801 2.331.801.324 0 .645-.039.963-.117.318-.078.621-.171.909-.279.288-.108.543-.228.765-.36.222-.132.393-.252.513-.36l.558 1.026zm-3.798-6.894a4.86 4.86 0 00-1.125.126 2.564 2.564 0 00-.936.423c-.27.198-.492.456-.666.774-.174.318-.285.711-.333 1.179h5.598c-.06-.792-.315-1.407-.765-1.845-.45-.438-1.041-.657-1.773-.657zm10.27-.036c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm0 7.074c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm20.216-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zM155.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM178.89 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zM189.358 7h2.142V5.218l1.404-.396V7h4.806v1.206h-4.806v4.248c0 .876.213 1.524.639 1.944.426.42 1.035.63 1.827.63.54 0 1.011-.102 1.413-.306a7.614 7.614 0 001.089-.666l.468 1.062c-.42.336-.921.606-1.503.81a5.377 5.377 0 01-1.791.306c-.48 0-.933-.069-1.359-.207a3.12 3.12 0 01-1.125-.639 3.06 3.06 0 01-.774-1.107c-.192-.45-.288-.987-.288-1.611V8.206h-2.142V7zm13.186.702a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zM214.542 3.4h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zM226.09 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm15.166 3.942a7.111 7.111 0 00-.891-.243 4.64 4.64 0 00-.945-.099c-1.176 0-2.052.276-2.628.828-.576.552-.864 1.428-.864 2.628 0 .528.084.999.252 1.413.168.414.408.765.72 1.053.312.288.687.51 1.125.666.438.156.921.234 1.449.234.564 0 1.113-.096 1.647-.288.534-.192.981-.444 1.341-.756l.63 1.044a5.095 5.095 0 01-.63.45 6.053 6.053 0 01-1.971.774 6.38 6.38 0 01-1.305.126c-.78 0-1.467-.111-2.061-.333a3.914 3.914 0 01-1.485-.954 4.083 4.083 0 01-.9-1.494 5.818 5.818 0 01-.306-1.935c0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.888 0 1.611.078 2.169.234.558.156 1.029.33 1.413.522l-.018.054v2.502h-1.296v-1.71zm4.708 2.124v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zM273.29 16v-1.206h3.132V8.206h-3.132V7h4.572v7.794h3.06V16h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm8.758 10.962l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm24.05-7.704a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V16h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zm8.884-4.806a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.412l.162 1.458h.09c.12-.204.279-.405.477-.603a3.741 3.741 0 011.575-.927 3.593 3.593 0 011.026-.144c.552 0 1.041.06 1.467.18.426.12.78.333 1.062.639.282.306.495.72.639 1.242.144.522.216 1.185.216 1.989V16h-1.404v-4.896c0-.996-.162-1.746-.486-2.25-.324-.504-.912-.756-1.764-.756-.312 0-.615.063-.909.189a3.137 3.137 0 00-.801.495c-.24.204-.45.441-.63.711-.18.27-.312.555-.396.855V16h-1.386v-5.85zm16.408-6.75h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm19.208-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm11.026-7.11h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zM389.436 3.4h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.192 3.51c0-1.452.378-2.601 1.134-3.447.756-.846 1.836-1.269 3.24-1.269.756 0 1.407.123 1.953.369s.999.579 1.359.999c.36.42.627.918.801 1.494.174.576.261 1.194.261 1.854 0 .72-.096 1.371-.288 1.953a4.057 4.057 0 01-.846 1.485 3.738 3.738 0 01-1.377.945c-.546.222-1.167.333-1.863.333-.744 0-1.392-.123-1.944-.369a3.815 3.815 0 01-1.368-.999 4.073 4.073 0 01-.801-1.494 6.381 6.381 0 01-.261-1.854zm1.494 0c0 .42.051.84.153 1.26.102.42.267.798.495 1.134.228.336.525.606.891.81.366.204.813.306 1.341.306.96 0 1.683-.297 2.169-.891.486-.594.729-1.467.729-2.619 0-.432-.051-.855-.153-1.269a3.353 3.353 0 00-.504-1.125 2.656 2.656 0 00-.9-.81c-.366-.204-.813-.306-1.341-.306-.96 0-1.68.294-2.16.882-.48.588-.72 1.464-.72 2.628zm11.422-8.1h2.988v9.576c0 .732.123 1.248.369 1.548.246.3.609.45 1.089.45.336 0 .657-.06.963-.18.306-.12.645-.324 1.017-.612l.648.99a3.988 3.988 0 01-.63.45 4.678 4.678 0 01-1.422.54c-.24.048-.462.072-.666.072a3.81 3.81 0 01-1.188-.171 2.03 2.03 0 01-.873-.549c-.234-.252-.411-.585-.531-.999-.12-.414-.18-.927-.18-1.539v-8.37h-1.584V3.4zm15.94 0h2.916v9.522c0 .108.006.243.018.405a20.058 20.058 0 00.108 1.044c.024.174.048.327.072.459h1.206V16h-2.394l-.18-1.386h-.072c-.264.456-.657.837-1.179 1.143-.522.306-1.125.459-1.809.459-1.356 0-2.355-.381-2.997-1.143-.642-.762-.963-1.947-.963-3.555 0-.756.108-1.425.324-2.007a3.873 3.873 0 01.927-1.458c.402-.39.885-.687 1.449-.891.564-.204 1.194-.306 1.89-.306.252 0 .477.006.675.018.198.012.381.03.549.054.168.024.327.057.477.099.15.042.315.087.495.135V4.606h-1.512V3.4zm-1.08 11.61c.732 0 1.308-.189 1.728-.567.42-.378.708-.945.864-1.701V8.566a2.788 2.788 0 00-.891-.396c-.33-.084-.765-.126-1.305-.126-.96 0-1.716.279-2.268.837-.552.558-.828 1.437-.828 2.637 0 .492.045.951.135 1.377.09.426.24.795.45 1.107.21.312.489.558.837.738.348.18.774.27 1.278.27zm7.408-4.5v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm3.718.216h2.88v4.626h.09a3.082 3.082 0 011.206-.918 3.94 3.94 0 011.602-.324c2.64 0 3.96 1.524 3.96 4.572 0 1.548-.426 2.733-1.278 3.555-.852.822-2.064 1.233-3.636 1.233a8.655 8.655 0 01-2.016-.216c-.6-.144-1.038-.312-1.314-.504V4.606h-1.494V3.4zm5.598 4.59c-.72 0-1.302.204-1.746.612-.444.408-.768.984-.972 1.728v4.14c.276.144.606.258.99.342a5.63 5.63 0 001.206.126c.48 0 .915-.069 1.305-.207.39-.138.723-.354.999-.648.276-.294.489-.669.639-1.125.15-.456.225-.996.225-1.62 0-.456-.048-.888-.144-1.296a3.146 3.146 0 00-.459-1.071 2.208 2.208 0 00-.819-.72c-.336-.174-.744-.261-1.224-.261zm7.66 7.416l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896zm11.116-4.896v-.522l8.478-5.166.684 1.116-7.146 4.23 7.164 4.158-.684 1.08-8.496-4.896zm19.108-7.326l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm5.41 6.966a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V7h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm7.426 7.308l-.684-1.08 7.164-4.158-7.146-4.23.684-1.116 8.478 5.166v.522l-8.496 4.896z"/></g><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M296 21l14 7-14 7-.001-6H46v-2h249.999l.001-6z"/><text id="mouse-move-direction" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="93" y="22">mouse move direction</tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/99-ui-misc/02-selection-range/selection-firefox.svg b/2-ui/99-ui-misc/02-selection-range/selection-firefox.svg new file mode 100644 index 0000000000..050852d3df --- /dev/null +++ b/2-ui/99-ui-misc/02-selection-range/selection-firefox.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="556" height="428" viewBox="0 0 556 428"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><path id="rect-1" d="M48 88h448v281H48z"/></defs><g id="selection-range" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="selection-firefox.svg"><g id="Bitmap"><image width="448" height="281" x="48" y="88" xlink:href="data:image/png;base64,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5YNp/frug8PwkowpRvFBfdGDnYJl4R0U4B9r6WAtH2wT7bE9b+Orc2vbZNug5TajhHytbxulDZmmTWb5oPKyUWOV16q6/rbZuGzK9pdXHfv2zYWrmJBJG1W2fpyD8y4zKStVHlla/N///Z+ZM2dOlWQffvih0eexxx7znKpus8023vmmTZuanj17eoNGRcjpqgQBf9AAXvEKcjzkv9Y//fST2WWXXYy2/qD6hw8f7n3uuusub1lbDVL9QfNKFXbeeeeE0Heoef311ytPS0DQ55lnnjGfffaZJz4cdthhlee1ozr1eeqppzyhQssI2yCLMvVXQSvWaDlUTYHxB9Wvj+a6ytFsFB88GnxroK3lOf1B/X711Ve9zwsvvGCefPLJtG/Q/Pm1r7I10JcAEwy6FvrccsstXrv33HPPKkkkekgc08odwfD+++8bfe69917z+OOPe1OD/GkkAllmo0ePNv37968i7rz77rtGHwkjYn7aaaet8qZN1+yCCy4wb731lvfJxGLD3yb2IQCBeAlITND9UoNrGzQY12+XlpQODkrznd62IY5t0DrRCgwSqoPTMew5+/xg25fP5whbR6pt8PrYtHrZqf7Zj9Klaqv6q98SBV17XWf11X7Cpo0nq9u2oVi29hnHPt/adttje97G22N73sbbY3vexrONj0BSJ5T2CywT+WyCHDjqgSksKD7ZubD0UeL0pq9bt27eG7Uo+fxpmzRZ6f/CsvCfi7qvB0g9ACZ74xi1PNf0GvDrLZt/AOCaNx/pfv99WpViNWiKEoIrZ7jkzcdyNC71+tNowGdveP74fO+rTv1YyTtwMQb7v+f646kfY5dVMPwsbNm2Lv+5XO3r+u+zzz6VxR1++OGmd+/enmn8v//9b2+Ar8G43oRra/9fNVh9+eWXvYHto48+apRPlg4KGtDqLbwNEjCs1ZZM7iV2WPFB/2fyd9GpUycjy4B//etfnhCi87LICAobtkwtdamw/fbbm7///e/m66+/9oQHDeaVV+3VfU1Bb+Q7duzoDb5f/0uwUDpZT9x+++1emuAfWShofq7m9ErEUH7dsx588EGvfbIW2G+//byBteuAWcv4+sWHAQMGeA9z+i267777vAc2DeYPPvhg89FHH0WaOnHHHXdUER90PWRN8sMPPxiZudolgPfaay/zzTffeNYXts8SJvzig6YI9erV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IqAIKAKKgCKgCCgCioAioAgoAoqAIqAIVIaACiAqw01jKQKKgCKgCCgCioAioAgoAoqAIqAIKAKKQBkItC0UCmaWWWYpI0p9B73sssvMk08+GTA5YMAA07Vr1/pmOEfu+vTpYyZPnmzatm1rrr76ajPbbLPlmLompQi0PgSOPvpo88033wTfyjXXXGNmnXXWigs5ceJEc8IJJwTxV1llFXPaaadVnFYlEfMsSyX55xHniiuuMI8//niQVL9+/czKK6+cR7J1kQblonzQXnvtZXbeeee64EuZSEbgyy+/NMcff7z53//+F7RH2mU1NGXKFPPvf//bjBs3zrz11lumQ4cOpkePHoY+Y++99zZzzz13Ncm3eNws/WBr6KtaHGhlQBGoUwSmfDnRXHXujLnQUsuvYvY6snnnQnUKi7IVQmAWK4AohNya9ScD8M8//1xVnosssohZdNFFgzT22Wcfc/PNNwfvjz32mNl0000rTvu3334LJh4vvfSSOeWUU8wuu+xScVrNEbFTp06GyRI0derUhp/I1BKzYcOGGRacO+ywg6l2QpkHn/XGTx5laoQ05p13XvPDDz8ErE6fPt3MPvvsFbM9fvx4061btyD+xhtvbEaPHl2UVq37kzzLUsR4M/448MADzfXXXx/k+NBDD5levXo1Y+61zYpF5xFHHBFkMnDgQHPGGWfUNkNNPRcE3n77bbPiiisGaW244Ybmqaeeqjhd+oQ99tjDj9PhhBph3E7rx9L6QcrcGvqqcN010m/mxv379zfLLbecufDCC818883XSOybe99uKHZnOmY/fm+8Oab3jLlQt7U2NueMKJ4LtTQgvTPuSzf6d9LSOKfl3zYtQK39e/bsGTsYZ82b3Ynzzz8/a/DM4ZgsXHLJJUH4vn37BjtWrUlbJDMQrSwgC052YKAXX3zR7LfffmbZZZdtsVLWGz8tBkQrz1j7k1ZewVo8RSABATZapPChffv2Zr311jNffPGFee2118yee+7ZEJsG2o8lVHKDeLHp8vTTTwfawptvvrk54IADGoRzZVMRaD4E9DupLdYtLoCobfGqS10KG7777jvT2o6rVIdO48YOq9n/9NNPLVqYeuOnJcG4++67zUcffRQIhRZYYIGWZCX3vLU/yR3SyARZ0N16662mS5cuZtttt40Mo46KQHMjwLFIp6G4wgorGI7kOM3NadOmGTSwGoFmxn6sUcalrHy2adPGNzW0bpQUAUWgFAH9TkoxydOlxW1AYKeBM9hRdN1115l33nkn8Nptt93MGmusERUs2EWI9KjScYMNNjA77bSTefbZZ81ZZ51lZGOsMmmN3oIIcMaWdjd06FDDkR3O3rYk1Rs/LYXF77//bvbff//gSMRGG21kWpsAQvuT5mlZd955p0Fjjb5bBRDNg7nmko7AM8884wNx/MYJH3Ccc845gz8foI5fZrZ+rFHGpXL4PPHEE82ECRMMgjDm1kqKgCJQioB+J6WY5OnS4hoQRx11VGx5sA/hBBAsFJu7o5xjjjnMyJEjY/lTj8ZFgPOP/NUL1Rs/LYELx2GcPYaWyL/WeWp/UmuEZ6TPLqCSIlBvCHzwwQeepXXWWce/N9rLzNaPNcq4VA6fCGf5U1IEFIF4BPQ7iccmD582Up0ujwQ1DUVAEVAEKkHggQceqCRabnG0L8wNyhZLCAHWqFGjWiz/rBlrW8uKVOsJJwUQCy20UOspWCsvSUuPS1nhbRQ+s5ZHwykCikDrRqDFNSCaA15UzbjJgqMUr7/+uuHWDKz/rr766qZ3796xRys4GnLllVcGLC6zzDKBAakofjEuddttt5mxY8cG59e5hooJxuKLLx7ks/XWW5vu3btHRc3sxtlRLLKSx5gxYwJjVWuvvbZZc801zSabbFK28Sp4xJr3G2+8YV555RWDjYtVV13VrLbaamattdZK5RejWW7AO/bYY4P8ObtPmhg3wp/0MLJFevI6PWxpoNlCfRAeTReMQC6//PKBoc8sWKFu+MQTTwR4fPjhh+b9998PztBSTxzV4XaLJZdcMhZfbp1gscKVpccdd1xJG3D+4LvFFlsY8gN3147effdd07lz58A6OoZUq7ltBSZdfnH8EAbcuBng0UcfDdrZJ598EuBOOfnbbLPNDFbaSaMaoo298MILQXlpH9QN18Sxa8ftDmnEGfwbb7wxqGPaxPfff28WW2yx4Hvglojtttsu+O3S4Tu79tpriwzJ8t2Br6TDDjvMzD///NIpqJdq2oFMjGtrf/3116DsDoNvv/02aLv0FRwL4ZuulLL2J+XiF8cPbZb6oy6fe+654BsB05VWWinoM/g2s5LDg2+gkjZBPpxzf+SRR4Jvlu+I748+jP4BbOmXqyH6nXPPPdcnwTWH5513nv/NC+1v++23L3JzP6rtE106WZ7sIkOffvppUDcvv/xygCvHjmhr9MN8a1mP/dFOMQ5IPbET+ssvvwRp0Advs802pmPHjlnYigxDncEfxI5Q2tXWV111lfn6668D3v/yl79EXgedx5iZV5m55YJxiLb96quvBv0dfR3jyPrrr5+5DiLBs460qwcffNDbfyDcxRdfbFwb4DfzEDlG4gZV2ybvu+++oF1R/4ccckiQ5ldffRXMa+gXGPfnmmsuQ9+a9ZavrP1YkFkF/7DJdOmllwbjHfOogw8+ODEV5g9O64n6ihujyu1XKxmXqsG70jlNNXwCLEa4Gdd+/PFHwzX2EAJSvt20W6G4EYVbNLiiFjr88MODa2WDH3/8IwxjNPNM+v3PPvssOPbKnIJ5U1p/ItNKe39//FjzzrgXzAdvjDEfvfeGWWTJZc0yXXuYFVZdx3AbQxTdO+ISM33aDDtgq6zd03TtsW5UsMDtm68mmUfvGu79t9r9UNO+Q6mtqglvv2bGv/Sk+eLTCWbSpx+YH7/7xsy34MJmuZXXMKut38t0WWVNn0b45cXH7zMfW97nW2Ah02vXGW2fcr396nNm/MtPmW8mTzLLd1/bdF11XbOC5XXBhRfzSfxq+/0Jb79q3hr7jHnD5v/Dt1+bxTuvYBZfpqvZbKcDgjR94NDL/TcNMz9P/cF06bam6bG+nfPaenv9xSfM++PHmHfHvWi+m/KVWXbl1c2yK65mVrZYLrLEMqEUKv9ZSb3J3L6Z/IV54t4bzecfvmO+/Pwj89OP3we4LNhpcbNUl25mzZ7bFeEk4ya9u++ZMO47keGj1kH0M8y33LwLG2+0ceZdaPKH57AyvZnu3XYcdl1Tn2Qt83JFaPB3++23Z2LS3qPt49gJaeEf//iH/+3Skk87AS7YRhSZtp1o+7h2UIsMM3z48IK1Zu3DybTlu13EFexgH5lGmqPtvBPzsJPMwscff1wgD5enNSwUm+xNN92UmB5pnHrqqQVrFCs2DXufvc/LDioFa/TN/3Y8yOfgwYODtOwCr3DQQQclhiXvuHZpB7PCoEGDCksssURiGuRtbzCJ5V/WWVQ5nb8VZBTA0lovT8zPTswLtuOJzS/Nw+UH31H82EVFwU5QE3kgLunQ7ish8rU3yiTmQd1ZgUJk8tStPTOXGN+1CfCErCAnU3ji8T06qkU7mDJlSmHLLbdM5CepH0rrL9L8K8HP4cFTtiG7uC/QJh3eUc999923YBeKMomS92rbhEuQ/om+NooP3OCdfu7II4/0YaywzUVPfdpbbXy8uDxwZ3yIojz6xKh0pZvsM+0CNLXPtIKSAm0yjexEJ7E/XHjhhQvlYBnOzx6T9NjaK67D3iW/Zd8cxX8eY2ZeZWaMSGovzEGssNyHsQLekvImORA+KX3nZ+1dlSSTR5t08yHaAMRY7fKUT3tFbEn+cQ5p/ViaP+nKvio83lmhnOeROU0ajRgxwoe316WXBK+kX610XKoE72rGsmr5pA3Yja0AMyu0LKqXe+65pwTLsAP9imtH1KkVLBYFsQK0gr1lw4dxYeXzggsuKFjhS1G8tB/3vFUoyL87Xpte2Omg5LnLFrscVLjl5e+L4pHGboed6vnrsODChRuf+7okDOHufvN/hXU228GHtQKNwsjXfy0Ke+qFtxascMCHkeWU7/By57hfiuK68myyfdMahvTTynXuDU8G6Yx45stC566rxuY9V7v2hVPOvzkyT/LGHx432maPAKe1N90+Ni3CnTTkxti0Lr23ae0ETq5s4Wc19UZa4LPLwdnmnJTL5Z/Wvpy/+54pr/tOnB9POabTtz788MOJmPGN2M05mcRM/Y7ksm4BqFYAEZ7wMghHLeIY5MIdJ6CkDaTW2FlJY7O7JgUmHXISRuOlE66Err/++pI8aMQIHUjX/VE2984zTgARtfgnrhReuHQoi9W8iGRbfnhWil2UN2WPSo9Jlr1qrCgs4aLCItCIItqrNZxUlAZ4UNdhd8phJZFRyRQNtOEJEBFIk/g8w5NI+I3KiwVdpeTyI88wP1bDw/Pj6ob8EYpF8VGJoIuFgt0RL8GV9CVv5I9bVPsKCy+IR73wF07DGnUNoGICRXsJfy+Ed+7uaVWYPby1aAdhLPkuwnxTfgRgUZTWX6T5V4Kf5EPyGq7LuDZLfxBHebQJ0ra7ypE40r9InsFW9mPlLJr79OkTtJdweq7tuCcYhymvPjGcbvi37DPhx33LPOE77IY7blaLKJyU/x01PsT1qfYWBh+vnJc8BRB5jJl5ldnu1hbVAXhHfSdyzlCuAII+xbU9Wd+0c+fO09qaKqqSvNqknEBbTYGS783xZLXqivJP+pHWj6X5k7b8TsPjXd4CiEr61UrHpUrwrmYsy4NPubBi88e1CeZqafSnP/3Jh7eapEXBrUZRUX9OurT78DiLO+mUQ24hyfOm56cUuq5WPHdhMW13/v2i2uELx2AAAEAASURBVJUJt9vHTvULUeLf8dq0wnLd1vDl6Nl73yJ/l9dfzr7ahyH9qx75sCTcdvuWCsIRCkQJBg45ZUhJfPKSAohV1ysW3sA/QhJXHp7wMujqh4LySveosPj/55EJkfk6AQTpSzyI03GRJUqwxH3vY86ITCuLAKLaegOrsHCGMiAA4s+Vx2Gy/3FneV6ztjX5PcvvxMWXY7rVdC+qF/o41mmyr3O8WE18l8RM/WzVAghX2X/961+LdpKQOCPxd/48hwwZUtIQ0gZSJtAuDTru8MKP3yykacTlTKYdI+w0y8ZLxy0bLpJ90pWTdsdP1AKRSY7z58luIxJqR+QXxoWdxSiSH55Lk4HeqgP64KRtrdAX5enCopkieWSRLRf6TMrihGMXXXRRMHljESsXpWTMbpWsF3a0o0jiGp4AEV76wzO/mfha9VCfHHhZa/tF5atU+0DmF+ZHTgooD7vJkmgHTCBZhFkVdOmV+Z1vxNUNvLD7IfHnt+TxzDPPLErbqvX6+KTDrhR8SWJiaVU8gzYxadIk6RW8yw6cyUsa5d0OXD2zSLLHBXz2LADD7dge+fH+7iWtv0jyzwM/WT+uLFna7PPPP++KUPSstk24xMLY0ZZk22BhFBaYwH8lfaY91uXbIRogaZRnn5iWV1SfySTfqiT7qAjC0TKg/O6PhXIU8Q25MDzDGi0IkKTAl/aRpvESlU+eAgjZN1cyZuZVZtlOwM4ePygSttPPR2lHlCuAkHjKcVqOfTIM73m2STmBdv0DfKDJhcYecyHaX3jMCfMkfyf1Y4RL8yeM4wXsw3nnKYDIo18tZ1yqFO88xrJK+ZQLK+aXsk+J0mCi/iDmPzKsPaY1w8P+Z+4gNwD57sNz17PPPrsovr2W1sdPe3FCAZ67H940d2HR2W/YPYG2ggvDb7kY3a/vmX4h6sIMu//NIl7CmgIs2mVZTx56U0kapHXhHWOCvHrvd2xh6G0vFO4a/7sPd9ML3xTsMYiidMLCENKQAgiXJ8IPBCWO3wtuf6lgj5cUpUVYynnaJXcWaWacM2J0kdBiy93/7NNx6fGUGLm0+g2722tqoAGCYCEsTLniwXdL0ssigKi23m54tnjOeeLgEUXlpkzXPvFp4aj+lxXW3GTbwvCnJnk+09qX85ffs/xOnH/UmI7WdHh+SNtmTePqk29VyZ4pr2cQqtWAoLLtedTYIg4cONA3CBpNmJIGUgZu15gYTGtB9vyyz4PGO3ny5MhsmEA4XtwzPMFhkGd3x/m7HeioBNEacOF4yoHDhQ9/eCxWoogJr0yLd+JGEQs9GVYKR2R4sE9S2bNnDX06cXWTNAEiL+kPT2+++aZkoegdTQTH9/nnn1/kl/WHzC88IZMTC3aT8yZrS8LzTzmi6ps8w+plcuEkJ/TueEW5fMpyZhFA5N0OKDs7SlHEgpk+wtWzPTNdEiypvyBwkn8e+Mk2BJ/jx48v4dE5yDaLCmyY8mgTpCnLBU8cJYoi2nxYCFFrAUTefWJUuaRbuM/885//LL2L3sO4hSc0BGbH0bVHe31tUXz5Qwp2OSJVLuUlgMhjzMyrzFIoZm/XKhKISXzuvfdejzFY11oAkXeblBNo+GdH7vPPP5dFLPs9qR8jsTR/wsi+Kjze5SmAkN9Rc4xLleKdx1hWzvgp+QwvrKTgIElrSh59oV1JuuOOO/x3w7wzPB91Yfv37+/DhdNwYaKebuF8zePFc5dL7nndLzJdGJ5nXVOsGn/t6M9Kwh17ZtOmJItxF2bkG78Vuq/TpOWLEEGmHX4PH8uQ/ggcpAbDZfeNL0krLIDo/+/7S8JElYnvO678HA1xYwWaEZIn9x4WQMRpStz68g9FGhJRAo00AUQe9Tb45qbNBnm8wpUn6RnVpqLckr4TwofH9CRNHjYJXR3wTFrDRPHSGt3aWCBaLdlFu7FCjNjyYVTEEYaoyiGMg9mdhCAKxtTsDn450TOFtVoZPpxd4JsFF1zQ/5YvGPjDkGIS3XXXXcZZ4baDv7HaCrHB11133SLcpGG3uEh29z3SC6NqduD3fmBmP1L/W74stdRSgVEi52Z3+t1r0ROjLknG2TBuJOsGI03VkFWHDYxNxqVx6KGHei+HsXfI4UVaTMewXt6EESlH3HuMob4owhinXSR6Lzu58+8dOnTw7xhywwherSnvdmB3kE2XLl0i2cawJ9+gI4xk2h0g97PqZ9748Y1h9CiOZJvFgGuY8mgTpOmMmvGOkTsMTkYRxs7s+e0or5q51bpPTGOcby2O+M7sgsJ7X3PNNf6dF4wSyzoKG9uUgc844wz/02qf+Pfmfql2zMyrzFbIWHRTSr9+/WIN92IwN8o4ZK2wq3WbpN9adNFFa8V+3aWbd79abgGz4p33WFYunzI8hiQdhfsd587Tatf5nzIOjgMGDPB+9FNzzz23/y1fMHTpCIN+VsPJ/cz0vPu6prmLtQVgll4+eu6CUUV7TMOn+dbYprmLc9xqj0PNulvMuJoUY4wXn35oYAj1vhsuMeNeeDwIZo8imCNOv9hFiXzOmmAEfI655jZrbrKdjzfpk6areb2jeFmqy8pmzY23ES5Nr5TJCg28gz06Elv+dTdvunL1M2uo0Wqo+HhRL9vufaTptHjnKC+b5zxmn2P6e7+Hbr8qMProHTK85FFv88zbNOec8NarBgOcLU2nn356LAsYiXfrEgKV29ZjE25gjzZY3m2thPABy/ZxJG9J4FaGcggrwb169fJRWLRjLTwvYnHDhMuRFJY4N/lk8ZREdgffeyN8iBsQXCApUHAW0J1f+Gl3U4tuNQj7czOFIyZ00vq3c3dPrPQ7sjs17rXsJzdqOKp2oYjl2iSSPE+wN67kTdxy4sjudJrLL788uK3BuVX7xHK+ox133NG9Rj6lAEJ+M1j5RbAF4c63wUS/pamcdpD2DW2wwQaBhXxXpjgBmfMv55k3flZyn5h9WpvNo03AABbqHdkjX+418mk1TCLda+VYyz4xjWerkpwoICK+tP4vvzX8pMAcS/JJi0pu1HBEOna31f1s1me1Y2ZeZX7vvfd8ue1ub3DDj3cIvcCz1VQJudbuZy3bJBsB3O4xM1He/Wo52OWNdzljWTl8hsNajSDv9OSTT5qoOQ1zM252cWSPernXYG6CMMGRnCc7N/fkRgAp4IsShruwUc/332yau6yzefLcZcUeTQIIbksIE996n0FXGquhEHi9PHqUuXzQsebKs4/zQU+54BbTrv18/nclLwsvvrSPNvWHb/171MtG2+4V3EgS5YfbYp2b5rmrbbhlXDAzmxXwu3IRiNsxkmj9XrskeZu1em5flB43T5RDedQbN3s4AQxClX6H9LK3YLTcnJOxJG4Dy2Ej27rVIHbOM+2zLZLX1kpSwBBVxrQrhqLiSDd2RNlRQgMCYQEdLRPLY445Jtj1dwsyGSfrO1csOrIqbCVXGzk/90TbI4nkpGvFFVdMChr4MXA7sqrcwXWHcW0lDed27dq5pMzSSzd1vt5RvEjM0oRjXNeFxgEDJAOXPa8YXPXHFW95Ln7RMEkiyXMtJvcM7uykcL0eZFWijVVdNNb+hDnwwAMTrxtN4tv52SMX7tVYy+uGq4fiiCtAHclFEYO3VUczbhHPxMXaLAl2vbnizdquiN1ldOlV+syrHaTVM/yhVeC0XPhGV1lllUrZLoqXN35p14Wmtdk82gQFlAKIJI0MwqJlQpuR7Qr3WlEt+8Q0ntP6QeJLwa3kFT+5KMDP2lPAOZKs6maRO9p6aROlogg5/qhmzMyrzFJwyDXLaYRmXnORrOe8x+mZTfhAneXdr5bTDirBO6+xrBw+w2ERCrB55zQcbrnlliLtP8Jb22Y+GoIWqaUpvy8CSW0IH0m8yLku8zZ7zEn4Jr9+/G7T3GX0fTeZl56In7u8+lzT3IUFaxRx9eWJg2+wi9kZi/n7b7zMB9u3z0Cz0uob+N9JL1xh+cVnH5ov7d/ET94339rrO6dP/9n8Mu1nM37MU0lRi/zkFZtFHn/8mHOuprn1QossGRXEu7Vr38F8+/WMTc3/pWw8L2g1PZKItUDnFXuYV55+KAj21ecfm84rdE+KUuSXR73xbR8z8Aoz5KR9g7S5dvSIbVYw62+5i9lqj8PM6htsaZK0UYoYyuGHHK/jkptvvibhVdr6Ji6N1uTelskJFdkaKW0SXm2Zu3fvHtwfTgfsJs3cJc6OCX9oGlgjjhVN9mSnnGUCJAeAqHLJhUAWXOadd95gRxvhCmTPZcYKD7Kk53jq1KmTe634iXTdGggLFuUVJ1JGxCwL0zKSKzsonRa7DWjBjBo1KoiPwAt1L/5of8cee6yRmhJZM2HCIzVt0K7ISuGjLWiK0JegjunajTXoaPhDQMYxIe6iz+se5LzbQZa2Kdu6tVmSFapM4fLEr5o2m1ebQPPItQMAyIIv7cT1pZlAqyJQLfvENLZkO4oLu8gii3ivsEDVCcEIQN+cdATDJ/LHi6yTsF+tf1czZuZVZvndZvlOsrTbvHCrZZuUmwp58dsI6eTZr5ZT3nLwznssK4fPqLAc4XMCCI5hyOOHhJdHM8JHasPHkWvVN03/+Se/oIanUTdnn7v8+P03RImk1TboZeztCuauay/w/hzf2POIv/vfcS9TvpxoHrztSnPn1UMMxziqpQ4dm8aAtLTm+0NzIy1cFv8FFl4sNdhCizYJZidPatowTYuYZ7317D1jznnJGYd7vJ996E7DH8dldjzwOLPlboeYeeabP42tqv3TNmKrzqAVJtC2tQofqCsW0bUmazjHWKNLxlouN9aSsd+lJl9r3C34w4bCCSecUNYOsL1Bw7Mudyu9Y+iF3UPCxU0urTFIHyPJfoIPZF/mmmsun541wie9it7nmWeeot9JP+K0KJLiSD922aNUuVFtQgKJzQn4GTZsmIxW1Xs55asqo4TILNrvv/9+88QTT5hLL73U3HbbbT407/yxa/Gvf/2rrAV+WIggz6j5DGJeoibmqP5zdh2NjYsvvjhYHBGdRdJJJ50ULJSsYSuz/fbbx6SazbkW7YD2nkZzzjmnDxL3rfkAFbzkhV+WPiOOvbzaRBifLBpnHTt2jGMrd/da9olpzGZpa/KoGljKzQIpNCSvcr5b2YbT+KzEP83+S6VjZl5l/vbbJrXnLN8JY0pzUS3bZJayNlc588wny05iXv1qOXxnxbsWY1k5fEaF5VgXwmDGbQTCaF+iYg5hh8odsaDfkbZq8LfG0nl4KqdvyooZiYeFCPKIgc885mX+BZM3wtrN07RTTRJzzDm3mcXafUui915/2Ry/e6mNI3tbhVl48c5mfitMmGvuecwrzz5s0mw/uHywt5CV2rRpXm12MHFUjrAl73qzRjvNGhttHQh+7h1xsZk86dOALZ5Xn3eSueOq80zff1xt1t60ujmnK2vcsznWm3F5N6p7q9aAaK5KYfHP2Xz+ODvNAtHevuGzRz0W1Up7xaV3S3uRO2RZjJWg+h+e8Ms8OCPsdpDCCwwZzr2TnpzwlTOIuDTyftpbGIqED/aavUDLBBsUYaGKvQ7TD5J589GS6fXs2dPwx/mx//znP2bo0KG+3tmxsFcqGoxAZl1khIUInLOWhrsqKSvCEgwKYmTvgQceCAQR7rwobap3797GWtAONDoqSb9W7QDe0rQz5LdYK4l3rfFLwzyvNhFOh/4pbYKZpW9K4z+rf0v2ibIdxfFrrxD0XhzDk5sF8ggH48s555zjw7bkC4tBOW7E8VLJmJlXmeVxxSy8SoFFXHnycm/JNplXGZo7nSzfEjy1dL8ahUutxrKovMpxY6OIo5Mc9YQ4mukEEGy2OUKjkW9ZkhwXOZKcZkNMxi3nvUNIiDBs1NtGGiYsJy0Z9q1XnjM3XjJAOpnX7PGN+2681Oywf58id/fj6y8/N3//02bup7FXVZq9jjrdrG1tJWB4UtI1g0+xi+LB0qnu3r/9+otULL/+YsZCH+Y7phz/kAWsRb2h4bDboaeYna0h0jFPPmAQRIx5aoaNEo6dnHlkb3u0ZoTZdIf9JCv63sIIJIv0Wpi5Rsx+9dVXDxaGSI5RjXfEjnA5NxjITlyqjLr0ws+0iZQUaMBbGtk7wn0QFg31IN1j0eoIbP/73/8Gi/Gw8IEw8rywi9OantQnkwMmXwMGDPBFY7dCqkd6j5gXJg/SME5Y1TsmWiZnJjFoOiCEsFe7GnY+HWHHIsvOlQsvn7VqB2nfEDzI861ZjkZJvst9rxV+aXzk1SZIRy72siwUyjVCllaWJP+W7BOzGKCSeEkDdJQJWxmOpL0O51arZ5r19PDuZxY+so6ZeZVZCjKyjIXVGEPOUn4ZpiXbpOSjXt7TtGngM8u3JMvTUv2q5MG912osc+lX85SGz2+44YbAeC3fPxsfjqJumZP2ZTiSnNZnuLTKfXK+n1siHH3+0bvuteLnTz9+b4ae3LRIXXOTbX1a/z6rr5nwdpNxTe9hX8Y+/X/+CAAaD/+49lFjr4YsET4QB7sQ9U7f/WErIolPaXhyocWajmMkxcGvFvXm8uTbRtNh4H8eMENueS4QBDm/ywZUPud0aegzXwRUAJEvnj41JhJIiu19495NWpb3jjEv8mwqCyNpHTsqirQQHuW/0UYbeWfO5KcR6v6OpBV159YSTwwbOsLOAJ1NFDFhTNIGiYrTqG7cZoIgQl6V+sILL5RVHHntJsKCWhC3xNj7xr2qOPUTPisq87V3w8ufRe+1agdpuCGUQ5DiqNYCCJcPz3Lxk3Erec+rTchFI8eHkog24bS0ksJl9UtbvLRkn4hKM7Y2kkh+i2EDV8stt5yPOnr0aFNLzRFpOCttscdxxEopbczMq8xSAIFmVtLxQsqSNvZWWt6oeC3ZJqP4aQk3qSWFgChtAVvNDnu5/WrSuFQJVrUay/Lgk++NoxgQc1C0Shn/nKAejQi5eREEtP/QlpV1KMvowuT1XKpLN58UO9/V0r//0dcfj8DuQ7/L7g40GVy6Q07cx2DDIEzvvPq8d2KXfd75F/S/wy9cGVnv9M5ryXPIzz96z7z/xhhfDGkPwjsmvORdb1FZde2xrjn7usf8bR0cE2kE4U9UWVqrmwogaliz7MxLw4BShTYtW64P5WiBo8sua7LG69zkE3sTScRxBUcPPfRQolocxy+kSq+M69JoieePP/7os5WTYu/4xwt2EGY2khPXcm/i2GabbTxcZ5xxRtk7Sj5yygtnqXv06OFDhfmUdZo0aalVO+C+8jBPnln7ct111/mfLKylkNB71PAlDb88s86rTXBcyNHgwYMDOwbud/gpj62F/bL+lhNfDLZOmzYtNqrs15q7T0TYIlWZw0xOnTq16MieWwi4cBi4c9olpDVo0CDnlftTLvzTFntDhgypKv+kMTOvMsuba1hM3XXXXbE8IySqtkyxiUd4tGSbjGCnRZycAWyXuTTM6dzckwUxC+NqKK1fzTouVcJDnmNZLfiUV9Defvvthj9HRxxxhHstee68887eDftnlWo7+kRiXtbYuGnucsNFZ5ivv6j8asPR991sHrmzaYw/4dzrg936vY7q53fSP35vvLl26F9LuJn281Tvxm0TcfTi4/eZuBs44uK0hPvdwy803OQRR/ff1LQeWbzzCiaL0UqZVp71JtMNv7fvsEBwW4dz//33+DK5MPpsPgRUAFEF1hyrkCrZ4aSYREpjgeVYRSatgQMH+iS59YHrkKIIo35cB5pE7Na6KxIJx3vUzg4TLu6fdzuRTHK5yaMeSC5eJa6SNzAqx+qyjFuv73fffXeiwAi+UZF0JNV4nVvSk6s8OavpaLvttjNJat3sSoUXT5xXxwaFNJ7q0nNPtHRY6DkK39wiVTfj6pe4tWoHlIvJUtTOOWd15VWHZ555ZondEVeuSp554FdJvnFx8mgTpN2nT9OZWY4HYYg0ajLKQgLhV7Ukd7dJC+2AOGrpPvFvf/ubeemll0rYow/GsjyCBQhh1+67714UDgOV2BpyhAD67LPPjhWgscvPFbrlHAN0aWN/whHffZSmELvU8ONu6XHhw89qxsy8yoyNGzm2cm22M6on+eU6Z+zYuHqQfrV6b+k2WatylZuuPK7HTU9RgmGOWVJ3SZRHv5p1XEriI84vz7GsFnxKQQJXb8p5xm677RZXrKAvcp4cw8A+mjSw6vzck++PMbZc2nynA81y3ZrmLgMO38589M7rsclgmBBBQ5i+/Owjw00KjvoMutIstnSX4Odss89uThpyo/MK7AsgSJC05LIr+Z+j7785cvGO5sNFpx3iw9XzCzj955wTzK+//FLC5qN3XV90Q8h+fcufC+VRb99N+crAy9Qfmgz2h5n9dMLb/qpQ/Oabf6FwEP3dggi06lswao0rxwCgLbfc0vTq1Su4hYHFHxNIJnpcaeiulGMhLwfVLLxtttlmgRaE2w3GkjMTPHYV2SWYOHFicDNC2qTP5cUOL4IKJlTwhfocCy7O4LJzCM8YGxo/fryLYs4///zgNgzv0IIvW2yxhV/AspuKGjODIIsOzktjE8IZO2xBNnPPGuOl9913X1Bf2L5gR5IyYxWfSRgLfxYXjhjsyyHO6rN4WH/99YNoTAa4Lo/znTxpu5ztRv2afMAdoj064jgFi1aIeKhndu7c2Sy44IKBejhtWGrx7LnnnsGNJS4+z/XWW8//JI9DDz3U7LLLLkFbxzbFVlttFfBSy3bAzR3sqMHfSiutZLhKEsOeCAAdRS0InV+lzzzwqzTvqHh5tAnSxfAbiz1nzIz+hAnprrvuGtTllClTgkV4Ode/RvHr3Lixhn7WLShpi+xg04YRCKMRIRfzLdknsvvOd8IVuggAUV3mCANCVDBydNZZZ0UeN9txxx2DI36u/z/ttNOCBQJ2V2i7EMfR+HbuuOOOoN/HaC3fdDmEJh5jhRsXUFtHGMeTm00YSxCSPv7446nJVjtm5lVmBGO0C8ZC6oGFIGVCO4IbSjAaPXz4cF/m1ILlGKAl22SOxagqKerHaTZwZJS2xkYItrGoLwTk8thhXGZ59KtZx6U4HpLc8xzLasEn/SnfLHMQcHdEv5pkrJr+ljnaySefHERBcMncgb6XvgRtDeavbOCNHDkyuG2Dfot5dDmEPYEj+11qTt57xtzlQ2uj4dgdu5vNdjrAdF6hu1mw0xLm+28nB5oRGJJ8d9yMuQs3Jzhip3+ItfvgbnJYZ7MdzJa7/9l5B8+ll+9m/vzX881VdlEODbZHMS63Ri8XWHjR4Pcq6zRp+pHHMb27mU3sFZHLrbyGwa7Em2OeLuua0CDRFv6HIcc3xz5tbVnsaZZYdsXg1hEwfMwu+h2h/bDB1sXCceeX9Myj3r747ENzwakz5pzU9/Ld1zad7G0j7TssaH787hvzxstPmvtvbNLU2GjbPa3fAklsqV9zI2AXMXVLtpMrWDyCP6v6lYlPuyjyceyZ89Q4Ln2eYbKTQZ+WnYSFvb2fTCPq3S7uC6RVCdkJZIG8o9KVbnaSX5B42cl2ZHZW66Fgd7RS04NnayApMg0c7dVRPg3yTiL8Ha/ESyI78fBhw3VuzzUW7OLC+7s0w09wsJNLH85OQkqypHwuHumGKc1fhreLBZ+WvZJKemV+T8rPDsw+fcdz3NMulDPnGQ5otRMKduKQOS8Z3+6KZo5nF/AFa09BRg/e7S5qYcMNN4xNxy60gnC1aAfhNhOHr10oFqLaE4yl9RdJ/nngl9SGAuDEv6xttpo24bKzi/6C/K7jsKWd26uMff2TdyVEvLg87IK/JMm8+sSShEMOss+kLWfpy8AjiazGUcFeSxxb3jAOVgCRlFysnxUgZsrDamEU5BhsBUxFaYb5iftNW44bM/Mqs9W6KVihT2q57r33Xh+O/qlSknnFjc8u7bzapKyLLPMhl3/SM6kfI16aP2HS+irGAsbSuPbh3BlL7KaKD2dvXyJ5T3n0q1nHJTItF+88x7Ja8WkFQR5fh/sjjzziMY57sVorBSvQL4nr0gg/6f+z0j1vFQryb9DVDxU6LpJ97iLj7n/cWZ7Hudq1Lwx/alJR2i7syDd+K3RfZ1Mf1hqoLNw1/ncftvd+x3q/cNncb2s0szDgylE+nL2Zwcd3+VjhiPc/e/hjJf4uHM9uazWtDS69943EsFZY4NMd/uTEkrCUHT7tdaaFQ05pmj873sNPq3lSuOqRD0vSgS94ceHhUfIs36upt6G3ZZ9zUvbrn/7C85G1naV9z3JMT1sHkac9YudxefbZZ7Oy0WrDtbGNpG5JGhlkRy4LYTvBUZY4diB0wUueMn+ZrguIStpBBx1UZHDH+fEkbdRsOeoQZaxHho1751ouJMf9+vULdvHC4dhBYwcc9WW360WYqJshcF9xxRWDHUd21dhxCxNu7LKjDSGtIIfDSWzlezgcv6W/fC83LLtt7LKhFhtF7Hwiqb/22muLdtOj8mSXy1GUbY40fxeXp2wnUXnJsHHvSfmhgkqZ2XmPI7QeOJ+dppIaFx93dh/YWerbt6+RatfhOGgHyLOg+HO8iF3sHXbYIRzc/2ZXBC0Idnej2h71wE4q139FkbtatBbtgDP2YMxustxJcnzA7/HHH2+eeuqpQPvEucunbAdR/UWSfx74JbUhySfvkpekNltNm3B5ojpvF9KBdlVUP0i7po2zEyaPAiXx5dKOesKzXTRG9pft2rUriZJXn1iScMjBlYe2hI0Ndnit8CDyW6AN0tbYCU4iNOGGDRsWaIbZxXFsUDDm2FCS2nRsZOuBjRnGMWlUWYYnb3Y5Ge+kjQXZzgifx5iZV5nZVbeL5eA4YngewG80Gzkiw86sU5WPG1clFlne09LJq03Kfsi1vyz8JYWRdSrTd3HS/AmX1lcxFvANo3kWrhviM5Yw97KT+EAzDjdI5s3vPPrVrOMS+Uk8suCd51hWKz7RjJTzAbCXtn0odxRRF2h3oYGFJmPUmE883OnnqjmCt9oGvcwl97xudjigr+EWijhiF/yv/2qyY4ExxREXnu6DnzTkBjN/x07+t3yhPMf981rv9PLoUebJ+5uORR/6twvM0QOGGbuQ92HcCzxxTeSQW543q667mXO27bV0XTNr26Y1TJS/j2xfZNg2lr8kQuvAkXx3bu7JtZW7HHKisYISgyHOMFkBhdnpoOPNuTc8ZRZefOmwd/Bb8iJ5DAeutN5IZ4llupqjB15u0FiJIyuUMkf1v8xceMcYb4wyLmyUe9r3LL9x+R6VFm58747CfZVzn5mesyBamZkKXIuyWslzoKqJOjEqvh07dgwM1HHGPe9GhrVzJk0M4Ez0mNxXSpyrRK2es++UgUlP+Fx+pWnXOp7dOTB2J9p88sknZpFFFgkW5/LjrnX+LZU+ZabOUCdHPZIjPwipalF28sBuA0eKWLjRrpkspLU5vgEEGag3c96f+oFP1PGzEsa5sEPC0RriMQHiOEeYatUOODPPgotvmkVz3OQpzE8ev/PALw8+otKotE3ItGhPYMsZe/qwJFVeGa/cd/o0jiDQjhjmOnXqZDgnnTRRaKk+kRtWOOLAN0Y/LCc+5ZSbctInuj6d/pw0o76dctKVYfnmwNT1vQi+y+U3zzEzrzJTHo6TsMDi2tM0IYHEpJbvLdUma1mmctPm++BYDPZ5uKGnkv44j34167hUbvkIn+dYVks+Kymbi8NRTvoOykq/7+YU5c6T733bpRj9nPbTVPOZPf8/fdpP9irMdvZWio7BAnS22SufL0fnVOrKnGeKNYY56dMJZvY55jRL2qtC544QSpTGbFmXPdec1x9DuXPcL6btH5u5v9m50CcfvBnc/rHoUsuZ+RaonR2FSuvtl+nTzEfvvh7w/z+L//wdF7HHbxY388wXPefs3bVlsdbcZyCgAghtCYqAIqAIKAKKgCKgCCgCioAiUPcIpAkg6r4AdcigFEDc8dp0g/HN1koqgKiPmq3rIxj1AZFyoQgoAoqAIqAIKAKKgCKgCCgCioAioAgoAtUioAKIahHU+IqAIqAIKAKKgCKgCCgCioAioAgoAoqAIpCKgAogUiHSAIqAIqAIKAKKgCKgCCgCioAioAgoAoqAIlAtAiqAqBZBja8IKAKKgCKgCCgCioAioAgoAoqAIqAIKAKpCKgAIhUiDaAIKAKKgCKgCCgCioAioAgoAoqAIqAIKALVItCWK6y4P1hJEVAEFAFFQBFQBBQBRUARUAQUAUVg5kFg0x32M1N/+NbMOmtb02bWWWeegmtJWwwBvYazxaDXjBUBRUARUAQUAUVAEVAEFAFFICsCeg1nVqQ0XBQCeg1nFCrN76ZHMJofc81REVAEFAFFQBFQBBQBRUARUAQUAUVAEZjpEFANiJmuyrXAioAioAgoAoqAIjCzIaA7xzNbjedXXt01zg9LTUkRUASMaYMNCCVFQBFQBBQBRUARUAQUAUVAEVAEFAFFQBFQBGqJgB7BqCW6mrYioAgoAoqAIqAIKAKKgCKgCCgCioAioAgECLTRGzC0JSgCioAioAgoAoqAIqAIKAKKgCKgCCgCikCtEVANiFojrOkrAoqAIqAIKAKKgCKgCCgCioAioAgoAoqA2oDQNqAIKAKKgCKgCCgCioAioAgoAoqAIqAIKAK1R0A1IGqPseagCCgCioAioAgoAoqAIqAIKAKKgCKgCMz0CKgAYqZvAgqAIqAIKAKKgCKgCCgCioAioAgoAoqAIlB7BNQIZe0x1hwUAUVAEVAEFAFFQBFQBBQBRUARUAQUgZkeAdWAmOmbgAKgCCgCioAioAgoAoqAIqAIKAKKgCKgCNQegba///67mXXWWWufU8459OnTx0yePNm0bdvWXH311Wa22WbLOQdNThFQBBQBRaCeEJg4caI54YQTApZWWWUVc9ppp9UTezXlZfTo0WbYsGFBHrvvvrvZbbfdapqfJq4IKALNi8C4Fx43D9xyRZDpxtvuZdbrtXPzMjCT5vb444+bK66Ygftee+1ldt5ZcZ9Jm4IWuxkRaNOIwgfwufXWW83NN99sRowYYX799ddmhEyzUgSyI/D555+bsWPHBn8fffRR9og25AcffODjfvHFF2XF1cCKQGtE4Jtvvgn6ffr+Bx98sOoiPvbYY2aTTTYxBx98sPnuu++qTq+WCbz33nu+7K+99lots2r4tOlrd911V7PddtuZ119/vcXLU2/8tDggykAkAp9NeMeMvu/m4G/C2/qNR4L0hyPC2HXWWccMGjQoKVgmv3feeUf71kxI1V+gRhrD6w+9luWobaFQMLPMMkvLcqG5KwKtFIGbbrrJnHTSSUHpFl544UCo0K5du9TSstBabbXVzA8//BCEveaaa8xBBx2UGk8DKAKKQHYE+vXrZ55++mnz5JNPms0339wccMAB2SNryLpF4NprrzV33nlnwN+iiy5qrrrqqhbltd74aVEwNHNFoEoEmBcdffTRQSovvvii2W+//cyyyy5bZaoavRER0DG8EWttBs9qhLJx6045/wOByy67zFx55ZV1icdRRx1lEDxAX375pbnkkksy8Xn++ed74cMKK6xg9t9//0zxNJAi0IgIoOFz8cUXm1GjRjUr+23aNJlBmjp1arPmrZnVDgG5qfLjjz/WLqOMKdcbPxnZ1mCKQF0iENbc/umnnyL5vPvuu4NxZcqUKZH+M6tja8JFx/DGbcVt0IBQUgQaFQHUa4855hhz+OGH12UR5p57bvPPf/7T8/aPf/zDoN2QRBzbOOuss3yQoUOHBrZOvIO+KAKtDAF2q/v27evP4TZX8U488USzxBJLBNoPalOhuVCvfT6c415jjTUMwtu//OUvtc8wJYd64yeFXfVWBOoaAeZVAwYMMO3btw/mftgDChP27di4YVwp9/hrOK3W9Lu14aJjeOO2zraNy7pyrgiYZt8xrQTzAw880Jx77rmGc4aoDg4ePNicffbZsUkhpHC08cYbm969e7uf+lQEWiUC7Mi0BO20006GP6XWhUDXrl3Nyy+/XDeFqjd+6gYYZUQRqBCB/v37G/7iiKMZ7ghrXJiZ0b214aJjeOO24ib908Ytg3I+EyNw11131X3puanlvPPO83yiEYGWQxS9++67hiMljhBWKCkCrRkBJonNffSiNeOpZVMEFAFFYGZH4IEHHpjZIYgsv+ISCYs6tgACdakBwVl5LJtye8CYMWMM6lZrr722WXPNNQOL5fwuh7i67amnnjJvvPGGeeWVVwJr56uuumpg5G+ttdYy3bt3z5wcR1ZIC9X/cePGBU/4QY0XIzi9evUKLPO6BN9++23jFsnwv8UWWzivyOfDDz8clBnPPfbYwyyzzDJF4e67776gHAsttFBguR1PcHruuecCviZNmhRg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xsJvoYUWct76bOUInHjiiQYNn80339zstttudVNa7cfrpiqUEUVAEVAEFAFFoKYI6BGMmsLbPIknHcFoHg5qk8t2221nRo0aFSwKR44cWZtMNNVWg0C1RzBaDRARBTnjjDPMoEGDAh8M1R588MERodRJEVAEFAFFQBFQBBQBRUARqC0CbSox1lZbljR1RcAExgQRPigpAopA9QhggNIRGh5KioAioAgoAoqAIqAIKAKKQEsg0LYezzgjFHnqqafM66+/bsaNGxc8Uc9EbRQDir169TLrrLNOLF7jx48PrpkjPmefuZ5y9dVXD66b23jjjc1iiy0WGzfsMXHixIAXrqx75ZVXDGfDubZutdVWM2uttVZwfV04jvxN/g888EDgdOyxxwY3VEyfPt3ceOON5uGHHzaTJk0K/DbddFMTdUMFt1tgtXzs2LFmzJgxQfy1117brLnmmmaTTTYpufFC5u3esSyOgS9wOO6440pUy++7777gSj7UsN3O6Lvvvmuef/754I+8F1hgAbPyyisH5d59990zGav7/PPPzYMPPmg++OCD4I/fGLsDu3XXXTew6xCl5o6BtHPPPdexH7SB8847z//mpVu3bmb77bf3bmll9AHtS7V1Wiu8JI9x79jwoD3QHvk2JkyYYLp27Rq0b65SpH1HYerSq7Y9unTinpwpR3D09ttvB7xhyBF+aDt8g7vuuqtp165dXPTAHdsffB/vvPOO+eijj8z3338ffLOLL754UO9oxpTzDcvMpk2bZi6//HLzyy+/BM58S5tttlnwzhWeV155ZfC+zDLLmD322ENGDd5dO+P7w2Al9hP4Lrk5A7sKfDedO3c2K664ounZs6fhu04jbt+4/fbbg/7lvffeC/oEykpfN//885dEB79jjjmmxD3K4ccffwzaizyCgSFK2o8jvsetttrK/fTPem1r4ToAvyeeeCKohxdffNF89dVXQVujXHwP1GUWcunQz1Ofn332WXBkrUePHkE98p3FUSXfVVQc2jtjH30g/ow12Or4//buA06PovD/+NwlBAhIDSV0CKGDgPTeIop0CZ2AlB9FUEG6fxCQpjQFQVACgVCkl9AhGoIg0gOEUCVA6C0oTfHy/Oe7yczN7u3u89xz89w9l3zm9Uqe3Z3d2d337vPczuwU/a3Rd8gF/Y3U90P3nNZ/8cUXk/tl8ODBZvvt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AAIIIIAAAggggAACxQIMw1lsQwwCCCCAAAIIIIAAAggggAACCEQSoBPKSJAkgwACCCCAAAIIIIAAAggggAACxQLUgCi2IQYBBBBAAAEEEEAAAQQQQAABBCIJMApGJEiSQQABBBBAAAEEEEAAAQQQQACBYgEKIIptiEEAAQQQQAABBBBAAAEEEEAAgUgCFEBEgiQZBBBAAAEEEEAAAQQQQAABBBAoFqAAotiGGAQQQAABBBBAAAEEEEAAAQQQiCTAKBiRIEkGAQQQQAABBBBAAAEEEEAAAQSKBagBUWxDDAIIIIAAAggggAACCCCAAAIIRBKgACISJMkggAACCCCAAAIIIIAAAggggECxAAUQxTbEIIAAAggggAACCCCAAAIIIIBAJAEKICJBkgwCCCCAAAIIIIAAAggggAACCBQLUABRbEMMAggggAACCCCAAAIIIIAAAghEEqAAIhIkySCAAAIIIIAAAggggAACCCCAQLEABRDFNsQggAACCCCAAAIIIIAAAggggEAkAQogIkGSDAIIIIAAAggggAACCCCAAAIIFAtQAFFsQwwCCCCAAAIIIIAAAggggAACCEQSaK1UKpGSIhkEEEAAAQQQQAABBBBAAAEEEEAgX6C1paUlP4alCCCAAAIIIIAAAggggAACCCCAQCQBmmBEgiQZBBBAAAEEEEAAAQQQQAABBBAoFqAAotiGGAQQQAABBBBAAAEEEEAAAQQQiCRAHxCRIEkGAQQQQAABBBBAAAEEEEAAAQSKBagBUWxDDAIIIIAAAggggAACCCCAAAIIRBKgE8pIkCSDAAIIIIAAAggggAACCCCAAALFAtSAKLYhBgEEEEAAAQQQQAABBBBAAAEEIgnQB0QkSJJBAAEEEEAAAQQQQAABBBBAAIFiAWpAFNsQgwACCCCAAAIIIIAAAggggAACkQQogIgESTIIIIAAAggggAACCCCAAAIIIFAsQCeUxTbEIIAAAggggAACCCCAAAIIIIBAJAFqQESCJBkEEEAAAQQQQAABBBBAAAEEECgWaG1rayuOJQYBBBBAAAEEEEAAAQQQQAABBBCIINBSsSFCOiSBAAIIIIAAAggggAACCCCAAAIIFAowDGchDREIIIAAAggggAACCCCAAAIIIBBLgE4oY0mSDgIIIIAAAggggAACCCCAAAIIFApQA6KQhggEEEAAAQQQQAABBBBAAAEEEIglwCgYsSRJBwEEEEAAAQQQQAABBBBAAAEECgUogCikIQIBBBBAAAEEEEAAAQQQQAABBGIJUAARS5J0EEAAAQQQQAABBBBAAAEEEECgUIACiEIaIhBAAAEEEEAAAQQQQAABBBBAIJYAo2DEkiQdBBBAAAEEEEAAAQQQQAABBBAoFKAGRCENEQgggAACCCCAAAIIIIAAAgggEEuAAohYkqSDAAIIIIAAAggggAACCCCAAAKFAhRAFNIQgQACCCCAAAIIIIAAAggggAACsQQogIglSToIIIAAAggggAACCCCAAAIIIFAoQAFEIQ0RCCCAAAIIIIAAAggggAACCCAQS4ACiFiSpIMAAggggAACCCCAAAIIIIAAAoUCFEAU0hCBAAIIIIAAAggggAACCCCAAAKxBCiAiCVJOggggAACCCCAAAIIIIAAAgggUChAAUQhDREIIIAAAggggAACCCCAAAIIIBBLoLVSqcRKi3QQQAABBBBAAAEEEEAAAQQQQACBXIHWlpaW3AgWIoAAAggggAACCCCAAAIIIIAAArEEaIIRS5J0EEAAAQQQQAABBBBAAAEEEECgUIACiEIaIhBAAAEEEEAAAQQQQAABBBBAIJYAfUDEkiQdBBBAAAEEEEAAAQQQQAABBBAoFKAGRCENEQgggAACCCCAAAIIIIAAAgggEEuATihjSZIOAggggAACCCCAAAIIIIAAAggUClADopCGCAQQQAABBBBAAAEEEEAAAQQQiCVAHxCxJEkHAQQQQAABBBBAAAEEEEAAAQQKBagBUUhDBAIIIIAAAggggAACCCCAAAIIxBKgACKWJOkggAACCCCAAAIIIIAAAggggEChAJ1QFtIQgQACCCCAAAIIIIAAAggggAACsQSoARFLknQQQAABBBBAAAEEEEAAAQQQQKBQoLWtra0wkggEEEAAAQQQQAABBBBAAAEEEEAghsD/BwDhXHdqSizjAAAAAElFTkSuQmCC"/><use stroke="#7E7C7B" xlink:href="#rect-1"/></g><path id="Line" fill="#C06334" fill-rule="nonzero" d="M309.315 59.871l1.064 1.694-.847.532-130.147 81.799 3.193 5.08L167 150.5l8.128-13.377 3.193 5.079 130.147-81.799.847-.532z"/><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M349.496 60.872l.004 1 .45 128.874 6.001-.02L349 204.75l-7.049-13.975 5.999-.022-.45-128.875-.003-1 2-.006z"/><path id="Line-Copy-2" fill="#C06334" fill-rule="nonzero" d="M386.71 57.027l.256.966 35.404 133.219 5.8-1.54L425 205l-10.361-11.732 5.798-1.542-35.403-133.22-.257-.966 1.933-.513z"/><text id="selection" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="20" font-weight="normal"><tspan x="295" y="44">selection</tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/99-ui-misc/03-event-loop/article.md b/2-ui/99-ui-misc/03-event-loop/article.md new file mode 100644 index 0000000000..19e09766b6 --- /dev/null +++ b/2-ui/99-ui-misc/03-event-loop/article.md @@ -0,0 +1,337 @@ + +# イベントループ(event loop): microtask と macrotask + +ブラウザの JavaScript 実行フローは、Node.js 同様 *event loop* に基づいています。 + +event loop の動作を理解することは最適化のためには重要であり、適切なアーキテクチャにとっても重要である場合があります。 + +このチャプターでは、最初にそれがどのように動作するかについて理論的な詳細を説明し、次にその知識の実践的な使用例を見ていきます。 + +## Event Loop + +*event loop* のコンセプトは非常にシンプルです。無限ループで JavaScript エンジンはタスクを待機し、それらを実行し、また次のタスクを待機します。 + +エンジンの一般的なアルゴリズムは次の通りです: + +1. タスクがある間: + - 最も古いタスクから開始し、それらを実行します。 +2. タスクが現れるまでスリープし、現れると 1. に進みます。 + +これは、ページを閲覧するときに見られることの形式化です。JavaScript エンジンはスクリプト/ハンドラ/イベントがアクティブになった場合にのみ実行され、ほとんどの時間何もしません。 + +タスクの例: + +- 外部スクリプト `<script src="...">` が読み込まれるとき、"タスク" はそれを実行することです。 +- ユーザがマウスを動かすとき、"タスク" は `mousemove` イベントをディスパッチし、ハンドラを実行することです。 +- `setTimeout` でスケジュールされた期限がくるとき、"タスク" はそのコールバックを実行することです。 +- ...等 + +タスクが設定され、エンジンがそれらを処理したあと、他のタスクを待機します(スリープ状態で CPU の消費はほぼゼロです)。 + +タスクはエンジンがビジーなときに来ることもあり、その時はキューに入れられます。 + +タスクはキュー、いわゆる "macrotask queue(マクロタスクキュー) (v8用語)" を形成します。 + +![](eventLoop.svg) + +例えば、エンジンが `script` の実行でビジーである間にユーザがマウスを移動させて `mousemove` を引き起こしたり、`setTimeout` の実行予定が来たりすると、これらのタスクは上の図に示すようにキューを形成します。 + +キューのタスクは "先着順" で処理されます。エンジンが `script` を完了させると、`mousemove` イベントを処理し、次に `setTimeout` ハンドラを実行していきます。 + +ここまではとても簡単ですね。 + +あと2つ詳細です: +1. エンジンがタスクを実行している間、レンダリングは発生しません。タスクが時間がかかるかどうかは関係ありません。DOM への変更はタスクが完了した後にのみ描画されます。 +2. タスクに時間がかかりすぎる場合、ブラウザは他のタスクの実行やユーザイベントの処理ができないため、しばらくすると "ページが応答していません" といった警告を表示し、ページ全体のタスクを強制終了するかどうかを訪ねます。これは複雑な計算が多数ある場合や、無限ループに陥るようなプログラムミスにより引き起こされます。 + +ここまでは理論でした。次からこの知識をどうのように適用できるか見ていきましょう。 + +## ユースケース1: CPUを大量に消費するタスクの分割 + +大量にCPUを食うタスクがあるとしましょう。 + +例えば、シンタックスハイライト(このページのコード例を色付けするために使用しています)は、かなりCPU負荷がかかります。コードをハイライトするために分析を行い、多くの色付けされた要素を生成し、ドキュメントに追加します。テキスト量が多い場合には多くの時間が必要です。 + +エンジンがシンタックスハイライトをするのに忙しい間は、他のDOM関連の処理やユーザイベントの処理などを行うことはできません。また、ブラウザが少しの間 "一時停止" したり "ハング" する可能性もありますが、これは受け入れられません。 + +この問題に関しては、大きなタスクを細かく分割することで回避が可能です。最初に 100 行をハイライト処理し、次に `setTimeout` (遅延ゼロで)で次の100行を処理するようスケジュールしていきます。 + +このアプローチのデモについては、簡単にするためにシンタックスハイライトではなく、`1` から `1000000000` までをカウントする関数を取り上げます。 + +以下のコードを実行すると、エンジンはしばらく "ハング" します。サーバサイドの JS の場合は顕著です。ブラウザで実行している場合には、ページ上の他のボタンをクリックしようとしてみてください。カウントが終了するまで、他のイベントが処理されないことが確認できます。 + +```js run +let i = 0; + +let start = Date.now(); + +function count() { + + // 重い処理を実行 + for (let j = 0; j < 1e9; j++) { + i++; + } + + alert("Done in " + (Date.now() - start) + 'ms'); +} + +count(); +``` + +ブラウザは "スクリプトの実行に時間がかかっています" という警告を表示するかもしれません。 + +ネストされた `setTimeout` を使ってこのジョブを分割しましょう: + +```js run +let i = 0; + +let start = Date.now(); + +function count() { + + // 重い処理の一部を実行 (*) + do { + i++; + } while (i % 1e6 != 0); + + if (i == 1e9) { + alert("Done in " + (Date.now() - start) + 'ms'); + } else { + setTimeout(count); // 新たな呼び出しをスケジュール (**) + } + +} + +count(); +``` + +これで "カウント" 処理の間もブラウザの操作は完全に機能します。 + +`count` を1回実行するとジョブ `(*)`の一部が実行され、必要に応じて `(**)` で再スケジュールが行われます。: + +1. 最初の実行カウント: `i=1...1000000`. +2. 2回目の実行カウント: `i=1000001..2000000`. +3. ...などなど. + +今、エンジンがパート1の実行でビジーな最中に新たな別のタスク(e.g. `onclick` イベント)が発生した場合、キューに入れられた後、パート1が終わったとき(次のパートが始まる前)に実行されます。`count` 実行間での event loop への定期的な戻りにより、JavaScript エンジンは他のユーザ操作に反応するための十分な "タイミング" を手にします。 + +注目すべき点は、両方のパターン(`setTimeout` でジョブを分割する場合とそうでない場合)の速度が同等であることです。全体をカウントする時間に大きな差はありません。 + +より差を近づけるために改善しましょう。 + +`count()` の先頭にスケジューリングの処理を移動させます: + +```js run +let i = 0; + +let start = Date.now(); + +function count() { + + // 先頭にスケジューリングを移動させる + if (i < 1e9 - 1e6) { + setTimeout(count); // 新たな呼び出しのスケジュール + } + + do { + i++; + } while (i % 1e6 != 0); + + if (i == 1e9) { + alert("Done in " + (Date.now() - start) + 'ms'); + } + +} + +count(); +``` + +これで、`count()` を開始しさらに `count()` が必要であることが分かると、ジョブを実行する前にすぐにスケジューリングします。 + +実行すると、時間が大幅に短縮されることが分かります。 + +なぜでしょう? + +簡単なことです: ご存知のように、多くのネストされた `setTimeout` 呼び出しの場合、ブラウザ内で 最小でも4msという遅延があります。たとえ `0` に設定しても `4ms` (またはそれ以上)になります。したがって、早くスケジュールするほど実行は早くなります。 + +これでCPUを大量に消費するタスクを分割できました。これでユーザインタフェースはブロックされません。そして全体の実行時間もそれほど長くありません。 + +## ユースケース2: 進行状況の表示 + +ブラウザスクリプトの重いタスクを分割するもう一つのメリットは進行状況を表示することができることです。 + +通常、ブラウザは現在のコードが完了した後にレンダリングします。タスクに時間がかかったかどうかは関係ありません。DOM への変更はタスクが終了した後にだけ行われます。 + +一方、これは素晴らしいことでもあります。なぜなら我々が作成した関数は多くの要素を生成したり、1つ1つドキュメントに追加したり、またそれらのスタイルを変更する可能性がありますが、訪問者がその "中間" -- 未完了状態を見ることはありません。これは重要なことです。 + +これはそのデモです。`i` への変更は関数が終了するまで見えません。なので、最後の値だけが見えます: + + +```html run +<div id="progress"></div> + +<script> + + function count() { + for (let i = 0; i < 1e6; i++) { + i++; + progress.innerHTML = i; + } + } + + count(); +</script> +``` + +...ですが、タスクの最中にプログレスバーなど何か表示したい場合もあります。 + +`setTimeout` を使って重いタスクを小さな単位に分割すると、それらの間で変更が描画されます: + +これはきれいに見えます: + +```html run +<div id="progress"></div> + +<script> + let i = 0; + + function count() { + + // 重い処理の一部を実行 (*) + do { + i++; + progress.innerHTML = i; + } while (i % 1e3 != 0); + + if (i < 1e7) { + setTimeout(count); + } + + } + + count(); +</script> +``` + +これで `<div>` にはプログレスバーのような、`i` の値の増加が表示されます。 + + +## ユースケース3: イベントの後になにかをする + +イベントハンドラの中では、イベントがバブルアップしてすべての階層で処理されるまでいくつかの処理を延期させることができます。遅延ゼロの `setTimeout` でラップすることで実現できます。 + +チャプター <info:dispatch-events> で見た例: カスタムイベント `menu-open` は `setTimeout` でディスパッチされるため、このイベントは "click" イベントが完全に処理された後に発生します。 + +```js +menu.onclick = function() { + // ... + + // クリックされたメニュー項目のカスタムイベントを作成 + let customEvent = new CustomEvent("menu-open", { + bubbles: true + }); + + // 非同期でカスタムイベントをディスパッチ + setTimeout(() => menu.dispatchEvent(customEvent)); +}; +``` + +## Macrotasks と Microtasks + +このチャプターで説明した *macrotasks* と併せて、チャプター <info:microtask-queue> で言及した *microtasks* が存在します。 + +Microtasks は通常は promise によって作成され、`.then/catch/finally` ハンドラの実行は microtask になります。Microtask は同様に promise ハンドリングの別の形の `await` の中でも使用されています。 + +Microtask キューで実行するために `func` をキューする `queueMicrotask(func)` という特別な関数もあります。 + +**すべての *macrotask* の直後に、エンジンは他の macrotask やレンダリングなどを実行する前に *microtask* キューにあるすべてのタスクを実行します。** + +例えばこれを見てください: + +```js run +setTimeout(() => alert("timeout")); + +Promise.resolve() + .then(() => alert("promise")); + +alert("code"); +``` + +ここでの順番はどうなるでしょう? + +1. 通常の同期呼び出しである `code` が最初に表示ます。 +2. `promise` が次に表示されます。なぜなら、`.then` は microtask キューを通じて、現在のコードの後に実行されるからです。 +3. macrotask である `timeout` が最後に表示されます。 + +より詳しいイベントループは次のようになります: + +![](eventLoop-full.svg) + +**すべての microtask は他のイベントハンドリングやレンダリング、または他の macrotask が行われる前に完了します。** + +これは microtask 間でアプリケーション環境が基本的には同じ(マウス座標の変更、新しいネットワークデータなどがないこと)であることを保証するため重要なことです。 + +(現在のコードの後に)関数を非同期に実行したいが、変更がレンダリングされたり新しいイベントが処理される前がよい場合は、`queueMicrotask` でスケジューリングすることができます。 + +ここに以前に示したものと類似の "カウントするプログレスバー" の例がありますが、`setTimeout` の代わりに `queueMicrotask` が使用されています。ここでは同期コードのように、レンダリングが最後に行われていることがわかります: + +```html run +<div id="progress"></div> + +<script> + let i = 0; + + function count() { + + // 重い処理の一部を実行 (*) + do { + i++; + progress.innerHTML = i; + } while (i % 1e3 != 0); + + if (i < 1e6) { + *!* + queueMicrotask(count); + */!* + } + + } + + count(); +</script> +``` + +## サマリ + +イベントループのより詳細なアルゴリズム([仕様](https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model)と比べると簡素化されていますが): + +1. *macrotask* キューにある最も古いタスク(e.g "スクリプト")を取り出して実行します。 +2. すべての *microtask* を実行します。 + - microtask キューが空でない間 + - 最も古い microtask を取り出して実行します。 +3. 変更がある場合はレンダリングします。 +4. macrotask キューが空であれば、macrotask が現れるまで待ちます。 +5. ステップ1 に戻ります。 + +新しい *macrotask* をスケジュールするには: +- 遅延ゼロの `setTimeout(f)` を使用します。 + +これは、ブラウザがユーザーイベントに反応したり、タスクの進捗状況を表示することができるよう、計算量の多いタスクを小さく分割するために使用されます。 + +また、イベントが完全に処理された(バブリングが完了した)後にアクションを行うようスケジュールするために、イベントハンドラ内でも使われることがあります。 + +新しい *microtask* をスケジュールするには: +- `queueMicrotask(f)` を使用します。 +- また、promise ハンドラは microtask キューで処理されます。 + +microtask 間では UI やネットワークイベントの処理はありません: これらはすぐに次々と実行されます。 + +```smart header="Web Workers" +イベントループをブロックしてはならない長く重い計算に対しては、[Web Workers](https://html.spec.whatwg.org/multipage/workers.html) を利用することができます。 + +これは並列スレッドでコードを実行する方法です。 + +Web Worker はメインプロセスとメッセージを交換することができますが、独自の変数とイベントループを持ちます。 + +Web Worker は DOM にはアクセスできないので、主に計算のために複数のCPUコアを同時に使用するのに役立ちます。 +``` diff --git a/2-ui/99-ui-misc/03-event-loop/eventLoop-full.svg b/2-ui/99-ui-misc/03-event-loop/eventLoop-full.svg new file mode 100644 index 0000000000..593cbab9b3 --- /dev/null +++ b/2-ui/99-ui-misc/03-event-loop/eventLoop-full.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="407" height="391" viewBox="0 0 407 391"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="promise" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="eventLoop-full.svg"><text id="..." fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="257.4" y="71">...</tspan></text><path id="Rectangle-1-Copy-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M216 86h108v28H216z"/><path id="Rectangle-1-Copy-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M216 195h108v28H216z"/><text id="mousemove" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="232.2" y="214">mousemove</tspan></text><path id="Rectangle-1-Copy-9" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M216 302h108v28H216z"/><text id="event-loop" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="44.922" y="189">event</tspan> <tspan x="51.115" y="222">loop</tspan></text><text id="render-copy-2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="297.3" y="176">render</tspan></text><g id="np_paint_2296353_000000-copy-2" fill="#C06334" fill-rule="nonzero" transform="translate(256 158)"><path id="Shape" d="M3.079 0A3.095 3.095 0 000 3.079V22.92A3.095 3.095 0 003.079 26H22.92A3.095 3.095 0 0026 22.921V3.08A3.095 3.095 0 0022.921 0H3.08zm0 2.053H22.92c.587 0 1.026.44 1.026 1.026v13.043l-2.886-2.684a1.027 1.027 0 00-1.443.032l-5.72 5.955-5.035-4.148a1.021 1.021 0 00-1.38.075l-5.43 5.43V3.08c0-.587.44-1.026 1.026-1.026zM6.158 3.42A3.095 3.095 0 003.078 6.5a3.095 3.095 0 003.08 3.079A3.095 3.095 0 009.237 6.5a3.095 3.095 0 00-3.08-3.079zm0 2.053c.579 0 1.026.447 1.026 1.026s-.447 1.026-1.026 1.026A1.012 1.012 0 015.132 6.5c0-.579.447-1.026 1.026-1.026zm14.176.566c-.377 0-.759.722-.759 1.08-.46.112-.748.358-.748.716s.371.717.748.717h2.662c.73 0 1.11-1.433-.021-1.433-.022-.677-.445-.86-1.134-.716-.183-.284-.375-.386-.748-.364zM16.71 9.237c-.314 0-.631.584-.631.876-.382.093-.63.296-.63.588 0 .293.316.589.63.589h2.213c.608 0 .931-1.176-.01-1.176-.018-.555-.379-.706-.952-.588-.152-.232-.31-.308-.62-.29zm3.688 6.382l3.55 3.293v4.01c0 .586-.44 1.025-1.027 1.025H3.08a1.01 1.01 0 01-.855-.449l6.05-6.04 4.202 3.453-1.24 1.294 1.475 1.422 7.687-8.008z"/></g><text id="microtasks-copy-2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="297.5" y="140">microtasks</tspan></text><g id="Group-Copy-2" fill="#C06334" transform="translate(256 124)"><path id="Fill-52" d="M23.145.978C21.737.348 20.186 0 18.56 0c-2.59 0-4.983.885-6.921 2.38L10.474.618a.286.286 0 00-.305-.125.303.303 0 00-.226.249L8.797 7.779a.317.317 0 00.07.258.287.287 0 00.22.102h.018l3.112-.206 3.504-.233a.296.296 0 00.258-.204.315.315 0 00-.078-.33l-1.46-1.415a7.18 7.18 0 014.118-1.301c1.07 0 2.083.238 3.003.662a.284.284 0 00.23.003.306.306 0 00.162-.171l1.35-3.568a.314.314 0 00-.159-.398" transform="matrix(-1 0 0 1 32.117 0)"/><path id="Fill-54" d="M12.787 13.315a12.66 12.66 0 001.346 4.7c1.225 2.393 3.101 4.186 5.274 5.27l-.929 1.908a.318.318 0 00.04.342c.077.094.201.13.315.09l6.454-2.269a.3.3 0 00.183-.187.32.32 0 00-.018-.251l-.01-.016-1.644-2.779-1.852-3.127a.29.29 0 00-.295-.142.298.298 0 00-.24.228l-.498 2.018a7.607 7.607 0 01-3.041-3.19 8.083 8.083 0 01-.865-3.087.314.314 0 00-.105-.214.286.286 0 00-.22-.068l-3.636.44a.305.305 0 00-.26.334" transform="matrix(-1 0 0 1 38.273 0)"/><path id="Fill-56" d="M2.646 22.333a12.003 12.003 0 003.205-3.576 12.684 12.684 0 001.713-7.424l2.04-.112a.293.293 0 00.262-.207.316.316 0 00-.084-.332L4.681 5.958a.28.28 0 00-.247-.072.291.291 0 00-.198.14l-.01.018-1.47 2.883-1.654 3.247a.32.32 0 00.03.339.285.285 0 00.307.104l1.916-.558a8.078 8.078 0 01-1.111 4.358 7.652 7.652 0 01-2.117 2.33.31.31 0 00-.123.203.325.325 0 00.053.234l2.183 3.08a.285.285 0 00.406.069" transform="matrix(-1 0 0 1 9.882 0)"/></g><text id="render-copy-2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="297.3" y="285">render</tspan></text><g id="np_paint_2296353_000000-copy-2" fill="#C06334" fill-rule="nonzero" transform="translate(256 267)"><path id="Shape" d="M3.079 0A3.095 3.095 0 000 3.079V22.92A3.095 3.095 0 003.079 26H22.92A3.095 3.095 0 0026 22.921V3.08A3.095 3.095 0 0022.921 0H3.08zm0 2.053H22.92c.587 0 1.026.44 1.026 1.026v13.043l-2.886-2.684a1.027 1.027 0 00-1.443.032l-5.72 5.955-5.035-4.148a1.021 1.021 0 00-1.38.075l-5.43 5.43V3.08c0-.587.44-1.026 1.026-1.026zM6.158 3.42A3.095 3.095 0 003.078 6.5a3.095 3.095 0 003.08 3.079A3.095 3.095 0 009.237 6.5a3.095 3.095 0 00-3.08-3.079zm0 2.053c.579 0 1.026.447 1.026 1.026s-.447 1.026-1.026 1.026A1.012 1.012 0 015.132 6.5c0-.579.447-1.026 1.026-1.026zm14.176.566c-.377 0-.759.722-.759 1.08-.46.112-.748.358-.748.716s.371.717.748.717h2.662c.73 0 1.11-1.433-.021-1.433-.022-.677-.445-.86-1.134-.716-.183-.284-.375-.386-.748-.364zM16.71 9.237c-.314 0-.631.584-.631.876-.382.093-.63.296-.63.588 0 .293.316.589.63.589h2.213c.608 0 .931-1.176-.01-1.176-.018-.555-.379-.706-.952-.588-.152-.232-.31-.308-.62-.29zm3.688 6.382l3.55 3.293v4.01c0 .586-.44 1.025-1.027 1.025H3.08a1.01 1.01 0 01-.855-.449l6.05-6.04 4.202 3.453-1.24 1.294 1.475 1.422 7.687-8.008z"/></g><text id="microtasks-copy-2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="297.5" y="249">microtasks</tspan></text><g id="Group-Copy-2" fill="#C06334" transform="translate(256 233)"><path id="Fill-52" d="M23.145.978C21.737.348 20.186 0 18.56 0c-2.59 0-4.983.885-6.921 2.38L10.474.618a.286.286 0 00-.305-.125.303.303 0 00-.226.249L8.797 7.779a.317.317 0 00.07.258.287.287 0 00.22.102h.018l3.112-.206 3.504-.233a.296.296 0 00.258-.204.315.315 0 00-.078-.33l-1.46-1.415a7.18 7.18 0 014.118-1.301c1.07 0 2.083.238 3.003.662a.284.284 0 00.23.003.306.306 0 00.162-.171l1.35-3.568a.314.314 0 00-.159-.398" transform="matrix(-1 0 0 1 32.117 0)"/><path id="Fill-54" d="M12.787 13.315a12.66 12.66 0 001.346 4.7c1.225 2.393 3.101 4.186 5.274 5.27l-.929 1.908a.318.318 0 00.04.342c.077.094.201.13.315.09l6.454-2.269a.3.3 0 00.183-.187.32.32 0 00-.018-.251l-.01-.016-1.644-2.779-1.852-3.127a.29.29 0 00-.295-.142.298.298 0 00-.24.228l-.498 2.018a7.607 7.607 0 01-3.041-3.19 8.083 8.083 0 01-.865-3.087.314.314 0 00-.105-.214.286.286 0 00-.22-.068l-3.636.44a.305.305 0 00-.26.334" transform="matrix(-1 0 0 1 38.273 0)"/><path id="Fill-56" d="M2.646 22.333a12.003 12.003 0 003.205-3.576 12.684 12.684 0 001.713-7.424l2.04-.112a.293.293 0 00.262-.207.316.316 0 00-.084-.332L4.681 5.958a.28.28 0 00-.247-.072.291.291 0 00-.198.14l-.01.018-1.47 2.883-1.654 3.247a.32.32 0 00.03.339.285.285 0 00.307.104l1.916-.558a8.078 8.078 0 01-1.111 4.358 7.652 7.652 0 01-2.117 2.33.31.31 0 00-.123.203.325.325 0 00.053.234l2.183 3.08a.285.285 0 00.406.069" transform="matrix(-1 0 0 1 9.882 0)"/></g><path id="Path" fill="#C06334" fill-rule="nonzero" d="M240.536 23.5c18.067 0 28.19 5.407 30.493 19.865l.067.441.016.179.264 10.223 5.906-11.165 2.652 1.402-8.582 16.229-1.247 2.357-1.367-2.29-9.412-15.76 2.576-1.539 6.476 10.845-.263-10.128-.046-.305c-1.998-12.562-10.586-17.259-27.028-17.353l-.505-.001h-51.072c-18.854 0-27.785 5.925-27.961 22.709l-.003.512v294.675c0 9.183 2.661 14.683 7.881 17.703 4.3 2.487 10.047 3.365 19.488 3.4l.595.001h51.072c9.796 0 15.693-.862 20.083-3.401 5.127-2.966 7.785-8.324 7.878-17.214l.003-.489v-6.472h3v6.472c0 10.183-3.152 16.697-9.379 20.3-4.884 2.825-11.037 3.766-20.972 3.803l-.613.001h-51.072c-10.296 0-16.602-.921-21.585-3.804-6.122-3.542-9.271-9.897-9.376-19.785l-.003-.515V49.721c0-18.944 10.246-26.078 30.352-26.219l.612-.002h51.072z"/><text id="script" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="245.3" y="105">script</tspan></text><text id="setTimeout" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="228.5" y="321">setTimeout</tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/99-ui-misc/03-event-loop/eventLoop.svg b/2-ui/99-ui-misc/03-event-loop/eventLoop.svg new file mode 100644 index 0000000000..ffe1976641 --- /dev/null +++ b/2-ui/99-ui-misc/03-event-loop/eventLoop.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="479" height="279" viewBox="0 0 479 279"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="promise" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="eventLoop.svg"><path id="Rectangle-1-Copy-5" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M218 173h108v28H218z"/><text id="..." fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="258.9" y="189">...</tspan></text><path id="Rectangle-1-Copy-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M218 90h108v28H218z"/><path id="Rectangle-1-Copy-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M218 117h108v28H218z"/><text id="mousemove" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="233.7" y="136">mousemove</tspan></text><path id="Rectangle-1-Copy-9" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M218 145h108v28H218z"/><text id="script" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="246.8" y="109">script</tspan></text><text id="event-loop" fill="#C06334" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="69.422" y="134">event</tspan> <tspan x="75.615" y="167">loop</tspan></text><text id="macrotask-queue" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="344.871" y="136">macrotask</tspan> <tspan x="367.951" y="169">queue</tspan></text><path id="Path" fill="#C06334" fill-rule="nonzero" d="M192 40c22.467 0 43.818 7.359 61.285 20.712l.622.479.143.122 8.961 8.422-4.584-14.61 3.817-1.197 6.869 21.895 1.064 3.392-3.452-.852-22.278-5.5.959-3.883 14.864 3.669-8.886-8.352-.55-.424c-16.564-12.656-36.757-19.698-58.036-19.87L192 44c-30.748 0-59.095 14.403-77.315 38.412l-.549.73-3.208-2.387C129.87 55.303 159.664 40 192 40z"/><path id="Path-Copy-2" fill="#C06334" fill-rule="nonzero" d="M269.882 208.148l2.823 2.834-.366.365a533.245 533.245 0 00-3.982 4.033l-.333.34c-9.922 10.12-14.79 14.544-22.017 19.272-15.185 9.934-34.01 15.688-51.594 15.688-25.222 0-47.144-6.827-64.077-19.673l-.589-.45-.089-.07-.08-.078-8.854-8.581 4.36 14.68-3.835 1.138-6.532-21.998-1.012-3.409 3.439.906 22.19 5.841-1.018 3.868-14.808-3.898 8.766 8.497.488.374c16.02 12.15 36.782 18.698 60.792 18.85l.859.003c16.796 0 34.862-5.522 49.404-15.035 6.911-4.521 11.624-8.806 21.35-18.725l.334-.34a633.126 633.126 0 013.46-3.511l.736-.736.185-.185z"/><text id="setTimeout" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="230" y="164">setTimeout</tspan></text></g></g></svg> \ No newline at end of file diff --git a/2-ui/99-ui-misc/index.md b/2-ui/99-ui-misc/index.md new file mode 100644 index 0000000000..0d27b0c77c --- /dev/null +++ b/2-ui/99-ui-misc/index.md @@ -0,0 +1,2 @@ + +# その他 diff --git a/3-animation/1-bezier-curve/article.md b/3-animation/1-bezier-curve/article.md deleted file mode 100644 index bec29a7e43..0000000000 --- a/3-animation/1-bezier-curve/article.md +++ /dev/null @@ -1,194 +0,0 @@ -# ベジェ曲線 - -ベジェ曲線は、CSSアニメーションやその他多くの場所で形状を描くために、コンピュータグラフィックスで使用されます。 - -実際には非常に単純なものですが、一度学んでおくと、ベクトルグラフィックスや高度なアニメーションの世界が受け入れやすくなるでしょう。 - -[cut] - -## 制御点(Control points) - -[ベジェ曲線](https://en.wikipedia.org/wiki/B%C3%A9zier_curve) は制御点/コントロールポイントによって定義されます。 - -制御点は 2, 3, 4 またはそれ以上あるかもしれません。 - -例えば、2つの制御点の曲線です: - -![](bezier2.png) - -3つの制御点の曲線です: - -![](bezier3.png) - -4点の曲線です: - -![](bezier4.png) - -これらの曲線をよく見るとすぐに気づくでしょう: - -1. **点は必ずしも曲線上にあるわけではありません。** それは全く普通のことであり、後で曲線がどうやって作られるのかを見ていきます。 -2. **曲線の次数は点の数から1を引いた数になります。** 2つの点の場合は線形曲線(直線)であり、3つの点の場合 -- 2次曲線(放物線), 4つの点の場合は -- 3次曲線を持ちます。 -3. **曲線は常に制御点の[凸包(convex hull)](https://en.wikipedia.org/wiki/Convex_hull) の内側にあります。:** - - ![](bezier4-e.png) ![](bezier3-e.png) - -最後の性質により、コンピュータグラフィックスでは、交差テスト(intersection tests)を最適化することができます。凸包が交差しない場合、曲線もまた交差しません。したがって、凸包の交差を最初にチェックすることで、非常に高速な "交差なし" の結果を得ることができます。交差や凸包のチェックはより簡単です。なぜなら、それらは四角形、三角形など(上の図を参照)であり、曲線よりもはるかに単純な図形だからです。 - -描画のためのベジェ曲線の主な価値は -- 点を動かすことによって、曲線が *直感的に分かりやすい方法で* 変化するということです。 - -下の例で、マウスを使って制御点を移動させて見ましょう: - -[iframe src="demo.svg?nocpath=1&p=0,0,0.5,0,0.5,1,1,1" height=370] - -**お気づきのように、曲線は接線 1 -> 2 and 3 -> 4 に沿って伸びます。** - -いくつかの練習を経て、必要な曲線を得るための点の配置の仕方が明らかになります。そして、複数の曲線を繋ぐことで、実際になにかを得ることができます。 - -ここではいくつかの例です: - -![](bezier-car.png) ![](bezier-letter.png) ![](bezier-vase.png) - -## Maths - -ベジェ曲線は数学的な式を使って記述することができます。 - -この後すぐに見ていきますが -- 知る必要はありません。が、完全性のためにここで説明します。 - -与えられた制御点の座標 <code>P<sub>i</sub></code>: 最初の制御点は座標 <code>P<sub>1</sub> = (x<sub>1</sub>, y<sub>1</sub>)</code> であり、2番目は: <code>P<sub>2</sub> = (x<sub>2</sub>, y<sub>2</sub>)</code> などとなり、曲線の座標は区分 `[0,1]` からのパラメータ `t` によって決まる式で記述されます。 - -- 2点の曲線の式: - - <code>P = (1-t)P<sub>1</sub> + tP<sub>2</sub></code> -- 3点の場合: - - <code>P = (1−t)<sup>2</sup>P<sub>1</sub> + 2(1−t)tP<sub>2</sub> + t<sup>2</sup>P<sub>3</sub></code> -- 4点の場合: - - <code>P = (1−t)<sup>3</sup>P<sub>1</sub> + 3(1−t)<sup>2</sup>tP<sub>2</sub> +3(1−t)t<sup>2</sup>P<sub>3</sub> + t<sup>3</sup>P<sub>4</sub></code> - -これらはベクトル方程式です。 - -これらを座標ごとに書き直すことができます。例えば、3点の曲線の場合: - -- <code>x = (1−t)<sup>2</sup>x<sub>1</sub> + 2(1−t)tx<sub>2</sub> + t<sup>2</sup>x<sub>3</sub></code> -- <code>y = (1−t)<sup>2</sup>y<sub>1</sub> + 2(1−t)ty<sub>2</sub> + t<sup>2</sup>y<sub>3</sub></code> - -<code>x<sub>1</sub>, y<sub>1</sub>, x<sub>2</sub>, y<sub>2</sub>, x<sub>3</sub>, y<sub>3</sub></code> の代わりに、3つの制御点の座標を入れます。 - -例えば、制御点が `(0,0)`, `(0.5, 1)` と `(1, 0)` の場合、方程式は次のようになります: - -- <code>x = (1−t)<sup>2</sup> * 0 + 2(1−t)t * 0.5 + t<sup>2</sup> * 1 = (1-t)t + t<sup>2</sup> = t</code> -- <code>y = (1−t)<sup>2</sup> * 0 + 2(1−t)t * 1 + t<sup>2</sup> * 0 = 2(1-t)t = –t<sup>2</sup> + 2t</code> - -今、 `t` が `0` から `1` まで実行されると、各 `t` の値 `(x,y)` の集合が曲線を形成します。 - -恐らくあまりに科学的な内容で、なぜ曲線がそのように見えるのか、そしてそれらが制御点にどのように依存するのかはあまり明白ではないと思います。 - -なので、ここでは理解しやすい描画アルゴリズムを紹介します。 - -## ド・カステリョのアルゴリズム - -[ド・カステリョ(De Casteljau's)のアルゴリズム](https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm) は曲線の数学的定義と同じですが、それがどのように構築されているかを視覚的に示しています。 - -3点の例を見てみましょう。 - -ここにデモとその説明が続きます。 - -点はマウスで動かすことができます。"play" ボタンを押して実行してみてください。 - -[iframe src="demo.svg?p=0,0,0.5,1,1,0&animate=1" height=370] - -**3点のベジェ曲線を構築するド・カステリョのアルゴリズムです。** - -1. 制御点を描画します。上のデモでは、それらは `1`, `2`, `3` とラベル付けされているものです。 -2. 制御点 1 -> 2 -> 3 の間に線分を作ります。上の例では、それらは <span style="color:#825E28">茶色の線</span> です。 -3. パラメータ `t` は `0` から `1` まで移動します。上の例では、一区切り `0.05` が使用されています。: ループは `0, 0.05, 0.1, 0.15, ... 0.95, 1` となります。 - - それぞれの `t` の値について: - - - 各 <span style="color:#825E28">茶色</span> の線分上では、その線の開始部分から `t` に比例した距離にある点を取ります。2つの線分があるので、2つの点があります。 - - 例えば、 `t=0` の場合 -- 両方の点は線分の先頭になり、`t=0.25` の場合は -- 開始から25%の線分の長さになります。`t=0.5` だと -- 50%(中央), `t=1` では -- 線分の終わりになります。 - - - 点を結びます。下の図では結んでいる線分は <span style="color:#167490">青</span> で描かれています。 - - -| `t=0.25` の場合 | `t=0.5` の場合 | -| ------------------------ | ---------------------- | -| ![](bezier3-draw1.png) | ![](bezier3-draw2.png) | - -4. 今、<span style="color:#167490">青</span> の線分は、同じ `t` の値に比例した距離で点を取ります。つまり、`t=0.25` (左図)の場合、線分の左端から1/4の位置に点があり、`t=0.5` (右図)の場合は -- 線分の中央に点があります。上の図では、点は <span style="color:red">赤</span> です。 - -5. `t` が `0` から `1` まで実行にするにつれて、`t` の各値は曲線に点を追加していきます。このような点の集合がベジェ曲線を形成します。それは上の図にある赤色の方物線です。 - -これは3点の場合の処理ですが、4点の場合も同様です。 - -4点の場合のデモです(マウスで点を移動させることができます): - -[iframe src="demo.svg?p=0,0,0.5,0,0.5,1,1,1&animate=1" height=370] - -アルゴリズム: - -- 制御点は 1 -> 2, 2 -> 3, 3 -> 4 の線分で結ばれています。ここでは、3つの <span style="color:#825E28">茶色</span> の線分があります。 -- `0` から `1` までの間隔における各 `t` に対して: - - 先頭から `t` に比例した距離で、それらの線分上の点を取ります。これらの点は結ばれ、2つの <span style="color:#0A0">緑の線分</span> ができます。 - - 緑の線分上で、`t` に比例した点を取ります。これで1つの <span style="color:#167490">青の線分</span> が得られます。 - - 青の線分上で `t` に比例した点を取ります。上の例では、それは <span style="color:red">赤</span> です。 -- これらの点が一緒に曲線を形成します。 - -このアルゴリズムは再帰的であり、任意の制御点の数で一般化できます。 - -与えられた N 個の制御点において、それらを結合し N-1 個の線分を得ます。 - -次に、`0` から `1` までのそれぞれの `t` について: -- 各線分上で `t` に比例した距離の点を取り、それらを結ぶと -- N-2 個の線分ができます。 -- N-2 個の線分に対して、`t` に比例した距離の点を取り、それらを結びます -- N-3 個のセグメントができます。 -- 1点だけを持つまで繰り返します。これらの点が曲線を作ります。 - -移動する曲線の例です: - -[iframe src="demo.svg?p=0,0,0,0.75,0.25,1,1,1&animate=1" height=370] - -その他の点の場合: - -[iframe src="demo.svg?p=0,0,1,0.5,0,0.5,1,1&animate=1" height=370] - -弧状の形: - -[iframe src="demo.svg?p=0,0,1,0.5,0,1,0.5,0&animate=1" height=370] - -滑らかではないベジェ曲線: - -[iframe src="demo.svg?p=0,0,1,1,0,1,1,0&animate=1" height=370] - -アルゴリズムが再帰的なので、5, 6, またはさらに多くの制御点を使用して、任意の順番のベジェ曲線を作ることができます。しかし、実際にはあまり有用ではありません。通常は 2, 3 の点を取り、複雑な線の場合は複数の曲線をくっつけます。開発と計算がよりシンプルになります。 - -```smart header="与えられた点を *通る* 曲線を描く方法?" -我々はベジェ曲線のために制御点を使います。ご覧の通り、制御点は曲線上にはありません。もしくはより正確に言うと最初と最後の制御点は曲線上にありますが、それ以外はありません。 - -別のタスクが必要なときもあります。: すべての制御点が単一の滑らかな曲線上にあるように、*複数の点を経由して* 曲線を描くというものです。このタスクは [補間/内挿](https://en.wikipedia.org/wiki/Interpolation) と呼ばれ、ここでは説明しません。 - -このような曲線の数式があります。例えば [ラグランジュ多項式](https://en.wikipedia.org/wiki/Lagrange_polynomial)です。 - -コンピュータグラフィックスでは、多くの点を結ぶ滑らかな曲線を構築するために[スプライン補間](https://en.wikipedia.org/wiki/Spline_interpolation) がよく使用されます。 -``` - -## サマリ - -ベジェ曲線は制御点により定義されます。 - -私たちはベジェ曲線の2つの定義を見てきました。 - -1. 数式を使った定義 -2. 描画プロセスを使った定義: ド・カステリョのアルゴリズム - -ベジェ曲線の良い特性: - - - 制御点を移動させることで、マウスで滑らかな線を描くことができます。 - - 複雑な形状は、いくつかのベジェ曲線で作ることができます。 - -利用法: - -- コンピュータグラフィックス、モデリング、ベクトルグラフィックエディタ。フォントはベジェ曲線で記述されています。 -- Web 開発では、-- Canvas 上のグラフィックスと SVG 形式で使われています。ちなみに、上の "ライブ" の例は SVG で書かれています。実際には、パラメータとして異なる点が与えられた単一のドキュメントです。別ウィンドウで開き、ソースを見ることができます: [demo.svg](demo.svg?p=0,0,1,0.5,0,0.5,1,1&animate=1). -- CSS アニメーションでは、アニメーションの経路とスピードを記述するために使われています。 diff --git a/3-animation/1-bezier-curve/bezier-car.png b/3-animation/1-bezier-curve/bezier-car.png deleted file mode 100644 index 5df005c3cc..0000000000 Binary files a/3-animation/1-bezier-curve/bezier-car.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier-car@2x.png b/3-animation/1-bezier-curve/bezier-car@2x.png deleted file mode 100644 index 990ecac3ea..0000000000 Binary files a/3-animation/1-bezier-curve/bezier-car@2x.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier-letter.png b/3-animation/1-bezier-curve/bezier-letter.png deleted file mode 100644 index 636c2abec1..0000000000 Binary files a/3-animation/1-bezier-curve/bezier-letter.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier-letter@2x.png b/3-animation/1-bezier-curve/bezier-letter@2x.png deleted file mode 100644 index 86e8f80ef1..0000000000 Binary files a/3-animation/1-bezier-curve/bezier-letter@2x.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier-vase.png b/3-animation/1-bezier-curve/bezier-vase.png deleted file mode 100644 index 1fdc08f766..0000000000 Binary files a/3-animation/1-bezier-curve/bezier-vase.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier-vase@2x.png b/3-animation/1-bezier-curve/bezier-vase@2x.png deleted file mode 100644 index e60ced0dee..0000000000 Binary files a/3-animation/1-bezier-curve/bezier-vase@2x.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier2.png b/3-animation/1-bezier-curve/bezier2.png deleted file mode 100644 index cbad0a367f..0000000000 Binary files a/3-animation/1-bezier-curve/bezier2.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier2@2x.png b/3-animation/1-bezier-curve/bezier2@2x.png deleted file mode 100644 index 6dbf1bfd83..0000000000 Binary files a/3-animation/1-bezier-curve/bezier2@2x.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier3-draw1.png b/3-animation/1-bezier-curve/bezier3-draw1.png deleted file mode 100644 index 177af24758..0000000000 Binary files a/3-animation/1-bezier-curve/bezier3-draw1.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier3-draw1@2x.png b/3-animation/1-bezier-curve/bezier3-draw1@2x.png deleted file mode 100644 index badd242dfb..0000000000 Binary files a/3-animation/1-bezier-curve/bezier3-draw1@2x.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier3-draw2.png b/3-animation/1-bezier-curve/bezier3-draw2.png deleted file mode 100644 index f27ebcd76c..0000000000 Binary files a/3-animation/1-bezier-curve/bezier3-draw2.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier3-draw2@2x.png b/3-animation/1-bezier-curve/bezier3-draw2@2x.png deleted file mode 100644 index 129eb36e2c..0000000000 Binary files a/3-animation/1-bezier-curve/bezier3-draw2@2x.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier3-e.png b/3-animation/1-bezier-curve/bezier3-e.png deleted file mode 100644 index f5b9e6284a..0000000000 Binary files a/3-animation/1-bezier-curve/bezier3-e.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier3-e@2x.png b/3-animation/1-bezier-curve/bezier3-e@2x.png deleted file mode 100644 index bc4da5f764..0000000000 Binary files a/3-animation/1-bezier-curve/bezier3-e@2x.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier3.png b/3-animation/1-bezier-curve/bezier3.png deleted file mode 100644 index 53d42f1d09..0000000000 Binary files a/3-animation/1-bezier-curve/bezier3.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier3@2x.png b/3-animation/1-bezier-curve/bezier3@2x.png deleted file mode 100644 index 80fef73285..0000000000 Binary files a/3-animation/1-bezier-curve/bezier3@2x.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier4-e.png b/3-animation/1-bezier-curve/bezier4-e.png deleted file mode 100644 index 1761ce21e6..0000000000 Binary files a/3-animation/1-bezier-curve/bezier4-e.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier4-e@2x.png b/3-animation/1-bezier-curve/bezier4-e@2x.png deleted file mode 100644 index b21b886982..0000000000 Binary files a/3-animation/1-bezier-curve/bezier4-e@2x.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier4.png b/3-animation/1-bezier-curve/bezier4.png deleted file mode 100644 index 9176f973fd..0000000000 Binary files a/3-animation/1-bezier-curve/bezier4.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/bezier4@2x.png b/3-animation/1-bezier-curve/bezier4@2x.png deleted file mode 100644 index f4e8d790bc..0000000000 Binary files a/3-animation/1-bezier-curve/bezier4@2x.png and /dev/null differ diff --git a/3-animation/1-bezier-curve/demo.svg b/3-animation/1-bezier-curve/demo.svg deleted file mode 100644 index fbf8e87d5a..0000000000 --- a/3-animation/1-bezier-curve/demo.svg +++ /dev/null @@ -1 +0,0 @@ -<?xml version="1.0" ?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/SVG/DTD/svg10.dtd"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:xlink="http://www.w3.org/1999/xlink" width="340" height="360"> <!--+ no-optimize --> <!-- @see http://www.learnsvg.com/books/learnsvg/html/bitmap/chapter04/page04-1.php http://apike.ca/prog_svg_paths.html http://www.the-art-of-web.com/css/css-animation/ http://srufaculty.sru.edu/david.dailey/svg/curve.svg http://www.w3.org/Graphics/SVG/IG/resources/svgprimer.html#path_C --> <defs> <pattern id="Pat01" width="10" height="10" patternUnits="userSpaceOnUse"> <rect width="10" height="10" fill="#FFFFFF" stroke="#000000" stroke-width="0.1"/> </pattern> </defs> <rect x="20" y="50" width="300" height="300" fill="url(#Pat01)"/> <path d="M20,50 H320 V350 H20 Z" fill="none" stroke="none" stroke-width="0.5" stroke-dasharray="2 2"/> <path fill="none" id="control-path" stroke="#E8C48E"/> <path stroke="red" fill="none" id="bezier-path" stroke-width="1.2"/> <path d="" fill="none" id="t-path-template"/> <svg x="0" y="0" id="p-template"> <circle r="4" fill="white" stroke="#E8C48E" stroke-width="1" cx="20" cy="20" style="cursor: pointer"/> <text x="12" y="12" style="font-size:16px;font-weight:bold">1</text> </svg> <circle r="4" fill="red" stroke="red" id="marker" stroke-width="1" cx="-10" cy="-10"/> <svg> <image xlink:href="play.png" x="5" y="5" style="cursor:pointer" width="30" height="30" id="run-button"/> <text x="38" y="27" style="font-size:18px;font-family:'DejaVu Sans Mono', 'Lucida Console', 'Menlo', 'Monaco', Lucida Console, sans-serif">t:<tspan id="t-value">1</tspan></text> </svg> <script><![CDATA[ let xmlns = "http://www.w3.org/2000/svg"; let xlinkns = "http://www.w3.org/1999/xlink"; let box = {left:20, top: 50, right: 320, bottom: 350 }; if (location.search.match('small=1')) { box = {left:20, top: 50, right: 120, bottom: 150 }; } document.documentElement.ondragstart = function() { return false; }; let pointsIn = location.search.match(/p=(.*?)($|&)/)[1]; // don't show a piece of control path between P2 and P3 let hideMiddleControlPath = location.search.match('nocpath=1'); pointsIn = pointsIn.split(','); let points = []; // read points from ?p=... for(let i=0; i<pointsIn.length; i++) { let x = box.left + (box.right - box.left)*pointsIn[i]; let y = box.bottom + (box.top - box.bottom)*pointsIn[++i]; points.push({x: x, y: y}); } let bezierPath = document.getElementById("bezier-path"); let controlPath = document.getElementById("control-path"); //controlPath.style.display = location.search.match('nocpath=1') ? 'none' : 'block'; let runButton = document.getElementById("run-button"); runButton.parentNode.style.display = location.search.match('animate=1') ? 'block' : 'none'; let tPathTemplate = document.getElementById("t-path-template"); tPathTemplate.parentNode.removeChild(tPathTemplate); function drawPath() { let letter; switch (points.length) { case 4: letter = 'C'; break; case 3: letter = 'Q'; break; default: letter = 'L'; } let bezierPathD = "M" + points[0].x + ',' + points[0].y + ' '+letter; let controlPathD = "M" + points[0].x + ',' + points[0].y + ' L'; for(let i = 1; i < points.length; i++) { bezierPathD += points[i].x + ',' + points[i].y + ' '; controlPathD += points[i].x + ',' + points[i].y + ' '; } bezierPath.setAttribute('d', bezierPathD); controlPath.setAttribute('d', controlPathD); function dist(a,b) { return Math.round(Math.sqrt( (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y))); } if (hideMiddleControlPath) { controlPath.setAttribute('stroke-dasharray', dist(points[0],points[1])+' '+dist(points[1],points[2])+' 9999 9999'); } } // draw control points function drawPoints(points) { let p = document.getElementById('p-template'); p.removeAttribute('id'); p.parentNode.removeChild(p); for(let i=0; i<points.length; i++) { let point = p.cloneNode(true); point.getElementsByTagName('text')[0].firstChild.data = i+1; setPointCoords(point, i); setPointHandler(point, i); document.documentElement.appendChild(point); } } // control points coords are shifted from Left-Upper corner a bit // to give them space to render function setPointCoords(point, i) { point.setAttribute('x', points[i].x-20); point.setAttribute('y', points[i].y-20); } function setPointHandler(point, i) { let circle = point.getElementsByTagName('circle')[0]; circle.onmousedown = function() { document.onmousemove = function(e) { let x = e.pageX, y = e.pageY; // constrain withing the box if (x < box.left) x = box.left; if (x > box.right) x = box.right; if (y > box.bottom) y = box.bottom; if (y < box.top) y = box.top; points[i].x = x; points[i].y = y; setPointCoords(point, i); drawPath(); } document.onmouseup = function() { document.onmousemove = document.onmouseup = null; } return false; } } // draw point connectors which form the curve function drawT(points, t) { let path = document.getElementById('t-'+points.length); if (!path) { path = tPathTemplate.cloneNode(true); path.setAttribute('stroke', ["blue","#0a0","red"][points.length % 3]); path.setAttribute('id', 't-'+points.length); document.documentElement.appendChild(path); } let subPoints = []; let x = points[0].x + (points[1].x - points[0].x)*t; let y = points[0].y + (points[1].y - points[0].y)*t; let tPathD = "M" + x + ',' + y + ' L'; subPoints.push({x, y}); for(let i=1; i<points.length-1; i++) { let x = points[i].x + (points[i+1].x - points[i].x)*t; let y = points[i].y + (points[i+1].y - points[i].y)*t; subPoints.push({x: x, y: y}); tPathD += x + ',' + y + ' '; } if (points.length <= 3) { let m = document.getElementById('marker'); let mx, my; if (t == 1) { mx = -10, my = -10; } else { mx = subPoints[0].x + (subPoints[1].x-subPoints[0].x)*t; my = subPoints[0].y + (subPoints[1].y-subPoints[0].y)*t; } m.setAttribute('cx', mx); m.setAttribute('cy', my); path.setAttribute('stroke-width', 2); } if (points.length == 2) { // only 2 points provided, special case tPathD = "M"+points[0].x+","+points[0].y+" L"+x+","+y; } path.setAttribute('d', tPathD); if (subPoints.length > 2){ drawT(subPoints, t); } } let t = 0; let timer; // animate the curve function animate(complete) { timer = setInterval(function() { if (t>1) { t = 1; } //bezierPath.setAttribute('stroke-dasharray', t*bezierPath.getTotalLength()+' '+bezierPath.getTotalLength()); drawT(points, t); document.getElementById('t-value').firstChild.nodeValue = t==0 ? 0 : t == 1 ? 1 : t.toFixed(3); /* if (t >= 0.5) { clearInterval(timer); runButton.parentNode.style.display = 'none'; return; } */ if (t == 1) { clearInterval(timer); timer = 0; complete && complete(); return; } t += 0.005; }, 30); } function animationDone() { for(let i=1; i<=points.length; i++) { let path = document.getElementById('t-'+i); path && document.documentElement.removeChild(path); } t = 0; runButton.setAttributeNS(xlinkns, 'xlink:href', 'play.png'); } runButton.onclick = onAnimate; function onAnimate() { if (timer) { // animation in action runButton.setAttributeNS(xlinkns, 'xlink:href', 'play.png'); clearInterval(timer); timer = 0; return; } runButton.setAttributeNS(xlinkns, 'xlink:href', 'pause.png'); animate(animationDone) } drawPath(); drawPoints(points); //]]></script> </svg> diff --git a/3-animation/2-css-animations/2-animate-logo-bezier-css/bezier-up.png b/3-animation/2-css-animations/2-animate-logo-bezier-css/bezier-up.png deleted file mode 100644 index 91e2bfe582..0000000000 Binary files a/3-animation/2-css-animations/2-animate-logo-bezier-css/bezier-up.png and /dev/null differ diff --git a/3-animation/2-css-animations/2-animate-logo-bezier-css/bezier-up@2x.png b/3-animation/2-css-animations/2-animate-logo-bezier-css/bezier-up@2x.png deleted file mode 100644 index 31384e53d9..0000000000 Binary files a/3-animation/2-css-animations/2-animate-logo-bezier-css/bezier-up@2x.png and /dev/null differ diff --git a/3-animation/2-css-animations/2-animate-logo-bezier-css/solution.md b/3-animation/2-css-animations/2-animate-logo-bezier-css/solution.md deleted file mode 100644 index a8d4fa6742..0000000000 --- a/3-animation/2-css-animations/2-animate-logo-bezier-css/solution.md +++ /dev/null @@ -1,7 +0,0 @@ -このアニメーションに対する正しいベジェ曲線を選ぶ必要があります。"飛び出す" ようにするため、どこかで `y>1` となるタイミングが必要です。 - -例えば、`cubic-bezier(0.25, 1.5, 0.75, 1.5)` のように、両方の制御点が `y>1` を取ることができます。 - -グラフは次の通りです: - -![](bezier-up.png) diff --git a/3-animation/2-css-animations/article.md b/3-animation/2-css-animations/article.md deleted file mode 100644 index 9d53e082a6..0000000000 --- a/3-animation/2-css-animations/article.md +++ /dev/null @@ -1,423 +0,0 @@ -# CSS アニメーション - -CSS アニメーションは JavaScritp を使うことなく簡単なアニメーションを行うことができます。 - -JavaScript を利用することで、CSS アニメーションを制御し、少しのコードでより優れたものにすることができます。 - -[cut] - -## CSS のトランジション - -CSS トランジションの考えはシンプルです。これから、そのプロパティとその変化がどのようにアニメーション化されるかを説明します。プロパティが変更されると、ブラウザはアニメーションを描写します。 - -つまり: 必要なことはプロパティを変更することだけです。そして滑らかなトランジションはブラウザによって行われます。 - -例えば、下の CSS は `background-color` の変化を 3秒間アニメーション化します。: - -```css -.animated { - transition-property: background-color; - transition-duration: 3s; -} -``` - -今、ある要素が `.animated` クラスを持っている場合、`background-color` の変更は3秒間でアニメーションされます。 - -下のボタンをクリックして、背景をアニメーションさせてみてください。: - -```html run autorun height=60 -<button id="color">Click me</button> - -<style> - #color { - transition-property: background-color; - transition-duration: 3s; - } -</style> - -<script> - color.onclick = function() { - this.style.backgroundColor = 'red'; - }; -</script> -``` - -CSS トランジションを記述するのに 5 つのプロパティがあります: - -- `transition-property` -- `transition-duration` -- `transition-timing-function` -- `transition-delay` - -この後説明していきますが、今の時点では、共通の `transition` プロパティは `property duration timing-function delay` の順番で一緒に宣言できること、複数のプロパティを一度にアニメーションすることができることに留意しておいてください。 - -例えば、このボタンは `color` と `font-size` をアニメーションします。: - -```html run height=80 autorun no-beautify -<button id="growing">Click me</button> - -<style> -#growing { -*!* - transition: font-size 3s, color 2s; -*/!* -} -</style> - -<script> -growing.onclick = function() { - this.style.fontSize = '36px'; - this.style.color = 'red'; -}; -</script> -``` - -ではアニメーションのプロパティを1つずつ見ていきましょう。 - -## transition-property - -`transition-property` には、アニメーションするプロパティの一覧を記載します。例えば: `left`, `margin-left`, `height`, `color` です。 - -すべてのプロパティがアニメーションできるわけではありませんが、[それらの多くが可能です](http://www.w3.org/TR/css3-transitions/#animatable-properties-)。値 `all` は "すべてのプロパティをアニメートする" を意味します。 - -## transition-duration - -`transition-duration` では、どのくらいの長さアニメーションをするかを指定することができます。時間は [CSS 時間形式](http://www.w3.org/TR/css3-values/#time) で表します: 秒は `s`, ミリ秒は `ms` です。 - -## transition-delay - -`transition-delay` では、アニメーションする *前* の遅延を指定することができます。例えば、`transition-delay: 1s` を指定した場合、アニメーションはある変更の1秒後に始まります。 - -負の値も可能です。その場合、アニメーションは途中から始まります。例えば、`transition-duration` が `2s` で、遅延が `-1s` の場合、アニメーションは1秒を取り、半分から開始します。 - -これは CSS `translate` プロパティを使って、`0` から `9` までの数字をシフトするアニメーションです: - -[codetabs src="digits"] - -`transform` プロパティは次のようにアニメーションされます: - -```css -#stripe.animate { - transform: translate(-90%); - transition-property: transform; - transition-duration: 9s; -} -``` - -上の例では、JavaScript は要素にクラス `.animate` を追加し -- それによりアニメーションを開始しています。: - -```js -stripe.classList.add('animate'); -``` - -"途中から" 始めることも可能です。負の値 `transition-delay` を使って、例えば、現在の秒数に対応する正確な数値から始めることができます。 - -ここでは、数字をクリックすると -- 現在の秒数からアニメーションが始まります。: - -[codetabs src="digits-negative-delay"] - -JavaScript は追加の行でそれをしています。: - -```js -stripe.onclick = function() { - let sec = new Date().getSeconds() % 10; -*!* - // 例えば、ここで -3s は3番目からアニメーションを開始します - stripe.style.transitionDelay = '-' + sec + 's'; -*/!* - stripe.classList.add('animate'); -}; -``` - -## transition-timing-function - -タイミング関数はアニメーションプロセスが時間と共にどのように広がっていくかを記述します。ゆっくりと始まりその急速に進む、またはその逆もありえます。 - -これは一見すると最も複雑なプロパティです。しかし少し時間をかけて見れば、非常に簡単のものになります。 - -このプロパティは2種類の値を受け取ります: ベジェ曲線またはステップです。より頻繁に使われる曲線から見ていきましょう。 - -### ベジェ曲線 - -タイミング関数は次の条件を満たす4つの制御点をもつ [ベジェ曲線](/bezier-curve) として設定できます。: - -1. 最初の制御点: `(0,0)`. -2. 最後の制御点: `(1,1)`. -3. 中間点については、`x` の値は区間 `0..1` になければならず、`y` は何でも構いません。 - -CSS でのベジェ曲線の構文です: `cubic-bezier(x2, y2, x3, y3)`。 -ここでは 2番目と3番目の制御点だけを指定します。なぜなら、最初の点は `(0,0)` 固定であり、4番目は `(1,1)` 固定だからです。 - -タイミング関数は時間の中でアニメーション処理がどのような速さで進むかを記述します。 - -- `x` 軸は時間です: `0` -- は開始時点、`1` -- は `transition-duration` の最後の瞬間です。 -- `y` 軸は処理の完了を指定します: `0` -- はプロパティの開始値であり, `1` -- は終わりの値です。 - -最もシンプルなバリアントは、同じ線形の速度でアニメーションが均一に進む場合です。それは曲線 `cubic-bezier(0, 0, 1, 1)` として指定することができます。 - -これは、その曲線がどのように見えるかを示したものです: - -![](bezier-linear.png) - -...ご覧の通り、単なる直線です。時間(`x`)が過ぎるに連れて、アニメーションの完了(`y`)は着実に `0` から `1` に進みます。 - -下の例にある電車は、左から右へ一定の速度で移動します(クリックしてみてください): - -[codetabs src="train-linear"] - -CSS `transition` はその曲線に基づいています: - -```css -.train { - left: 0; - transition: left 5s cubic-bezier(0, 0, 1, 1); - /* JavaScript sets left to 450px */ -} -``` - -...そして電車をスローダウンさせるにはどうすれば良いでしょうか? - -別のベジェ曲線を使うことで実現できます: `cubic-bezier(0.0, 0.5, 0.5 ,1.0)`. - -グラフは次のようになります: - -![](train-curve.png) - -見てわかるように、処理は速く始まります: 曲線は高くなっていき、その後遅くなっていきます。 - -タイミング関数は次のように動作します(電車をクリックしてください): - -[codetabs src="train"] - -CSS: -```css -.train { - left: 0; - transition: left 5s cubic-bezier(0, .5, .5, 1); - /* JavaScript sets left to 450px */ -} -``` - -いくつかの組み込みの曲線があります: `linear`, `ease`, `ease-in`, `ease-out` そして `ease-in-out` です。 - -`linear` は `cubic-bezier(0, 0, 1, 1)` を簡略したものです -- それは直線であり、先程見たものです。 - -その他の名前は以下の `cubic-bezier` の簡略表記です: - -| <code>ease</code><sup>*</sup> | <code>ease-in</code> | <code>ease-out</code> | <code>ease-in-out</code> | -|-------------------------------|----------------------|-----------------------|--------------------------| -| <code>(0.25, 0.1, 0.25, 1.0)</code> | <code>(0.42, 0, 1.0, 1.0)</code> | <code>(0, 0, 0.58, 1.0)</code> | <code>(0.42, 0, 0.58, 1.0)</code> | -| ![ease, figure](ease.png) | ![ease-in, figure](ease-in.png) | ![ease-out, figure](ease-out.png) | ![ease-in-out, figure](ease-in-out.png) | - -`*` -- デフォルトでは、タイミング関数がない場合 `ease` が使用されます。 - -したがって、スローダウンする電車に対しては、`ease-out` を使うことができました。: - -```css -.train { - left: 0; - transition: left 5s ease-out; - /* transition: left 5s cubic-bezier(0, .5, .5, 1); */ -} -``` - -しかし、実際は少し異なって見えます。 - -**ベジェ曲線はアニメーションがその範囲から "飛び出す" ようにすることができます。** - -曲線上の制御点は、負または巨大な値の `y` 座標を持つことができます。すると、ベジェ曲線も非常に低くまたは高くジャンプし、アニメーションが通常の範囲を超えます。 - -下の例のアニメーションコードは次の通りです: -```css -.train { - left: 100px; - transition: left 5s cubic-bezier(.5, -1, .5, 2); - /* JavaScript sets left to 400px */ -} -``` - -プロパティ `left` は `100px` から `400px` までアニメーションするはずです。 - -しかし、電車をクリックすると、次のようになります: - -- まず、電車は *バック* します: `left` は `100px` よりも小さくなります。 -- 次に前に進み、`400px` よりも少し先に進みます。 -- その後再びバックし -- `400px` になります。 - -[codetabs src="train-over"] - -なぜこのようなことが起きるのでしょう? -- 与えられたベジェ曲線を見れば明らかです: - -![](bezier-train-over.png) - -2番目の `y` 座標がゼロより下に移動し、3番目の点は `1` を越えています。そのため、曲線は "通常" の象限から外れています。`y` は "標準" の範囲 `0..1` から外れています。 - -ご存知の通り、`y` は "アニメーション処理の完了" を表します。値 `y = 0` は開始プロパティ値に対応し、`y = 1` は -- 終わりの値に対応します。そのため、値 `y<0` はプロパティを開始時の `left` よりも小さい値に移動させ、`y>1` は -- 最後の `left` を越えます。 - -これは確実に "ソフトな" バリアントです。もし `y` の値を `-99` や `99` といった値にした場合、電車はその範囲から遥か遠くに飛び出します。 - -しかし、特定のタスクのためのベジェ曲線はどうやって作るのでしょう?そのための多くのツールがあります。例えば、<http://cubic-bezier.com/> などで行うことができます。 - -### Steps - -タイミング関数 `steps(number of steps[, start/end])` はアニメーションをステップに分割することができます。 - -数字を使った例を見てみましょう。私たちは数字を滑らかではなく、離散的に変化させます。 - -そのために、アニメーションを 9 つのステップに分割します: - -```css -#stripe.animate { - transform: translate(-90%); - transition: transform 9s *!*steps(9, start)*/!*; -} -``` - -`steps(9, start)` の動作です: - -[codetabs src="step"] - -最初の `steps` の引数はステップの数です。変換は 9 つのパートに分割されます(それぞれ 10%)。時間間隔も同様に分割されます: 9秒は1秒間隔になります。 - -2つ目の引数は `start` または `end` いずれかの単語です。 - -`start` はアニメーション開始時にすぐに最初のステップを行うことを意味します。 - -アニメーションでそれが確認できます。数字をクリックすると、すぐに `1` (最初のステップ) に変わり、以降は次の秒のはじめに変化していきます。 - -プロセスはこのように処理されます: - -- `0s` -- `-10%` (1秒目の頭で最初の変更がされます, 開始直後) -- `1s` -- `-20%` -- ... -- `8s` -- `-80%` -- (最後の秒は最後の値を表示します)。 - -もう1つの値 `end` は、変化は各秒の最初ではなく最後に適用されるようにすることを意味します。 - -したがって、処理は次のように進みます: - -- `0s` -- `0` -- `1s` -- `-10%` (最初の変更は1秒目の最後です) -- `2s` -- `-20%` -- ... -- `9s` -- `-90%` - -`steps(9, end)` の動作です: - -[codetabs src="step-end"] - -簡略表記もあります: - -- `step-start` -- は `steps(1, start)` と同じです。つまり、アニメーションはすぐに始まり、ステップは1つです。なので、これは開始後すぐに終わり、まるでアニメーションがないかのように見えます。 -- `step-end` -- は `steps(1, end)` と同じです。: `transition-duration` の終わりに単一ステップのアニメーションを行います。 - -これらの値はほとんど使われません。なぜなら、実際にはアニメーションではなく、単なる単一ステップの変更だからです。 - -## transitionend イベント - -CSS アニメーションが終了すると、`transitionend` イベントがトリガされます。 - -これはアニメーション完了後になにかをするのに広く使われています。また、アニメーションを付け加える事もできます。 - -例えば、下の例にある船は、クリックで往復し始めます。時間が経つごとにどんどん右に行きます。 - -[iframe src="boat" height=300 edit link] - -アニメーションは関数 `go` によって開始され、遷移が終了して方向を反転する度に `go` が再実行されます: - -```js -boat.onclick = function() { - //... - let times = 1; - - function go() { - if (times % 2) { - // 右に進みます - boat.classList.remove('back'); - boat.style.marginLeft = 100 * times + 200 + 'px'; - } else { - // 左に進みます - boat.classList.add('back'); - boat.style.marginLeft = 100 * times - 200 + 'px'; - } - - } - - go(); - - boat.addEventListener('transitionend', function() { - times++; - go(); - }); -}; -``` - -`transitionend` のイベントオブジェクトはいくつかのプロパティを持っています。: - -`event.propertyName` -: アニメーションを終了したプロパティ。複数のプロパティを同時にアニメーションした場合に便利です。 - -`event.elapsedTime` -: `transition-delay` を除く、アニメーションの時間(秒単位)。 - -## キーフレーム(keyframes) - -`@keyframes` という CSS のルールを使用して、複数の簡単なアニメーションを一緒に動作させることができます。 - -この方法では、アニメーションの "名前" と、何を/いつ/どこでアニメーションさせるかのルールを指定します。その後、`animation` プロパティを使ってアニメーションと要素の紐づけを行い、追加のパラメータを指定していきます。 - -これは説明付きの例です: - -```html run height=60 autorun="no-epub" no-beautify -<div class="progress"></div> - -<style> -*!* - @keyframes go-left-right { /* 名前を指定します: "go-left-right" */ - from { left: 0px; } /* left: 0px からアニメーションを開始します */ - to { left: calc(100% - 50px); } /* left: 100%-50px までアニメーションします */ - } -*/!* - - .progress { -*!* - animation: go-left-right 3s infinite alternate; - /* アニメーション "go-left-right" を要素に適用します - 期間は 3 秒 (3s) - 回数: 無限 (infinite) - 順方向/逆方向を毎回交互に (alternate) - */ -*/!* - - position: relative; - border: 2px solid green; - width: 50px; - height: 20px; - background: lime; - } -</style> -``` - -`@keyframes` や [詳細な仕様](https://drafts.csswg.org/css-animations/) について多くの記事があります。 - -ただし、あなたのサイト上で常に動いているものがない限り、恐らく `@keyframes` を頻繁に必要とはしないでしょう。 - -## サマリ - -CSS アニメーションは、1つ以上の CSS プロパティの変更をなめらかにアニメーション化できます。 - -これはほとんどのアニメーションのタスクに適しています。なお、アニメーションに JavaScript を使うこともできます。次のチャプターではそれを見ていきます。 - -JavaScript アニメーションと比較した場合の CSS アニメーションの制限は次の通りです: - -```compare plus="CSS animations" minus="JavaScript animations" -+ 簡単なことは簡単にできます。 -+ CPU に対し高速であり軽量です。 -- JavaScript アニメーションは柔軟です。 要素の "爆発" のような任意のアニメーションロジックを実装することができます。 -- 単なるプロパティの変更ではありません。JavaScript ではアニメーション目的で新しい要素を作成すると言ったことが可能です。 -``` - -大部分のアニメーションはこのチャプターで説明した CSS を使用して実装することができます。そして `transitionend` イベントはアニメーションの後に JavaScript を実行することができるので、コードともうまく統合できます。 - -しかし、次のチャプターではより複雑なケースを取り扱うため、 JavaScript アニメーションをいくつか見ていきます。 diff --git a/3-animation/2-css-animations/bezier-linear.png b/3-animation/2-css-animations/bezier-linear.png deleted file mode 100644 index 795e4cdf4d..0000000000 Binary files a/3-animation/2-css-animations/bezier-linear.png and /dev/null differ diff --git a/3-animation/2-css-animations/bezier-linear@2x.png b/3-animation/2-css-animations/bezier-linear@2x.png deleted file mode 100644 index 95ec3cf913..0000000000 Binary files a/3-animation/2-css-animations/bezier-linear@2x.png and /dev/null differ diff --git a/3-animation/2-css-animations/bezier-train-over.png b/3-animation/2-css-animations/bezier-train-over.png deleted file mode 100644 index bd48229072..0000000000 Binary files a/3-animation/2-css-animations/bezier-train-over.png and /dev/null differ diff --git a/3-animation/2-css-animations/bezier-train-over@2x.png b/3-animation/2-css-animations/bezier-train-over@2x.png deleted file mode 100644 index 8dad7cc346..0000000000 Binary files a/3-animation/2-css-animations/bezier-train-over@2x.png and /dev/null differ diff --git a/3-animation/2-css-animations/ease-in-out.png b/3-animation/2-css-animations/ease-in-out.png deleted file mode 100644 index 50ed8a7bb6..0000000000 Binary files a/3-animation/2-css-animations/ease-in-out.png and /dev/null differ diff --git a/3-animation/2-css-animations/ease-in-out@2x.png b/3-animation/2-css-animations/ease-in-out@2x.png deleted file mode 100644 index 067ad9baef..0000000000 Binary files a/3-animation/2-css-animations/ease-in-out@2x.png and /dev/null differ diff --git a/3-animation/2-css-animations/ease-in.png b/3-animation/2-css-animations/ease-in.png deleted file mode 100644 index e00c52c45f..0000000000 Binary files a/3-animation/2-css-animations/ease-in.png and /dev/null differ diff --git a/3-animation/2-css-animations/ease-in@2x.png b/3-animation/2-css-animations/ease-in@2x.png deleted file mode 100644 index 14207250c3..0000000000 Binary files a/3-animation/2-css-animations/ease-in@2x.png and /dev/null differ diff --git a/3-animation/2-css-animations/ease-out.png b/3-animation/2-css-animations/ease-out.png deleted file mode 100644 index b95ce55054..0000000000 Binary files a/3-animation/2-css-animations/ease-out.png and /dev/null differ diff --git a/3-animation/2-css-animations/ease-out@2x.png b/3-animation/2-css-animations/ease-out@2x.png deleted file mode 100644 index df4cca89dc..0000000000 Binary files a/3-animation/2-css-animations/ease-out@2x.png and /dev/null differ diff --git a/3-animation/2-css-animations/ease.png b/3-animation/2-css-animations/ease.png deleted file mode 100644 index 2d4d91cdd2..0000000000 Binary files a/3-animation/2-css-animations/ease.png and /dev/null differ diff --git a/3-animation/2-css-animations/ease@2x.png b/3-animation/2-css-animations/ease@2x.png deleted file mode 100644 index 49eeb9606b..0000000000 Binary files a/3-animation/2-css-animations/ease@2x.png and /dev/null differ diff --git a/3-animation/2-css-animations/train-curve.png b/3-animation/2-css-animations/train-curve.png deleted file mode 100644 index 213ed84a63..0000000000 Binary files a/3-animation/2-css-animations/train-curve.png and /dev/null differ diff --git a/3-animation/2-css-animations/train-curve@2x.png b/3-animation/2-css-animations/train-curve@2x.png deleted file mode 100644 index 4d8d698dad..0000000000 Binary files a/3-animation/2-css-animations/train-curve@2x.png and /dev/null differ diff --git a/3-animation/3-js-animation/1-animate-ball/solution.md b/3-animation/3-js-animation/1-animate-ball/solution.md deleted file mode 100644 index 3a9b8564f6..0000000000 --- a/3-animation/3-js-animation/1-animate-ball/solution.md +++ /dev/null @@ -1,21 +0,0 @@ -To bounce we can use CSS property `top` and `position:absolute` for the ball inside the field with `position:relative`. - -The bottom coordinate of the field is `field.clientHeight`. But the `top` property gives coordinates for the top of the ball, the edge position is `field.clientHeight - ball.clientHeight`. - -So we animate the `top` from `0` to `field.clientHeight - ball.clientHeight`. - -Now to get the "bouncing" effect we can use the timing function `bounce` in `easeOut` mode. - -Here's the final code for the animation: - -```js -let to = field.clientHeight - ball.clientHeight; - -animate({ - duration: 2000, - timing: makeEaseOut(bounce), - draw(progress) { - ball.style.top = to * progress + 'px' - } -}); -``` diff --git a/3-animation/3-js-animation/1-animate-ball/task.md b/3-animation/3-js-animation/1-animate-ball/task.md deleted file mode 100644 index 903e823843..0000000000 --- a/3-animation/3-js-animation/1-animate-ball/task.md +++ /dev/null @@ -1,9 +0,0 @@ -importance: 5 - ---- - -# Animate the bouncing ball - -Make a bouncing ball. Click to see how it should look: - -[iframe height=250 src="solution"] diff --git a/3-animation/3-js-animation/2-animate-ball-hops/solution.md b/3-animation/3-js-animation/2-animate-ball-hops/solution.md deleted file mode 100644 index d47387e30a..0000000000 --- a/3-animation/3-js-animation/2-animate-ball-hops/solution.md +++ /dev/null @@ -1,32 +0,0 @@ -In the task <info:task/animate-ball> we had only one property to animate. Now we need one more: `elem.style.left`. - -The horizontal coordinate changes by another law: it does not "bounce", but gradually increases shifting the ball to the right. - -We can write one more `animate` for it. - -As the time function we could use `linear`, but something like `makeEaseOut(quad)` looks much better. - -The code: - -```js -let height = field.clientHeight - ball.clientHeight; -let width = 100; - -// animate top (bouncing) -animate({ - duration: 2000, - timing: makeEaseOut(bounce), - draw: function(progress) { - ball.style.top = height * progress + 'px' - } -}); - -// animate left (moving to the right) -animate({ - duration: 2000, - timing: makeEaseOut(quad), - draw: function(progress) { - ball.style.left = width * progress + "px" - } -}); -``` diff --git a/3-animation/3-js-animation/2-animate-ball-hops/task.md b/3-animation/3-js-animation/2-animate-ball-hops/task.md deleted file mode 100644 index 1134d19218..0000000000 --- a/3-animation/3-js-animation/2-animate-ball-hops/task.md +++ /dev/null @@ -1,13 +0,0 @@ -importance: 5 - ---- - -# Animate the ball bouncing to the left - -Make the ball bounce to the left. Like this: - -[iframe height=250 src="solution"] - -Write the animation code. The distance to the right is `100px`. - -Take the solution of the previous task <info:task/animate-ball> as the source. diff --git a/3-animation/3-js-animation/article.md b/3-animation/3-js-animation/article.md deleted file mode 100644 index 99775934c1..0000000000 --- a/3-animation/3-js-animation/article.md +++ /dev/null @@ -1,460 +0,0 @@ -# JavaScript animations - -JavaScript animations can handle things that CSS can't. - -For instance, moving along a complex path, with a timing function different from Bezier curves, or an animation on a canvas. - -[cut] - -## setInterval - -From the HTML/CSS point of view, an animation is a gradual change of the style property. For instance, changing `style.left` from `0px` to `100px` moves the element. - -And if we increase it in `setInterval`, by making 50 small changes per second, then it looks smooth. That's the same principle as in the cinema: 24 or more frames per second is enough to make it look smooth. - -The pseudo-code can look like this: - -```js -let delay = 1000 / 50; // in 1 second 50 frames -let timer = setInterval(function() { - if (animation complete) clearInterval(timer); - else increase style.left -}, delay) -``` - -More complete example of the animation: - -```js -let start = Date.now(); // remember start time - -let timer = setInterval(function() { - // how much time passed from the start? - let timePassed = Date.now() - start; - - if (timePassed >= 2000) { - clearInterval(timer); // finish the animation after 2 seconds - return; - } - - // draw the animation at the moment timePassed - draw(timePassed); - -}, 20); - -// as timePassed goes from 0 to 2000 -// left gets values from 0px to 400px -function draw(timePassed) { - train.style.left = timePassed / 5 + 'px'; -} -``` - -Click for the demo: - -[codetabs height=200 src="move"] - -## requestAnimationFrame - -Let's imagine we have several animations running simultaneously. - -If we run them separately, each one with its own `setInterval(..., 20)`, then the browser would have to repaint much more often than every `20ms`. - -Each `setInterval` triggers once per `20ms`, but they are independent, so we have several independent runs within `20ms`. - -These several independent redraws should be grouped together, to make it easier for the browser. - -In other words, this: - -```js -setInterval(function() { - animate1(); - animate2(); - animate3(); -}, 20) -``` - -...Is lighter than this: - -```js -setInterval(animate1, 20); -setInterval(animate2, 20); -setInterval(animate3, 20); -``` - -There's one more thing to keep in mind. Sometimes when CPU is overloaded, or there are other reasons to redraw less often. For instance, if the browser tab is hidden, then there's totally no point in drawing. - -There's a standard [Animation timing](http://www.w3.org/TR/animation-timing/) that provides the function `requestAnimationFrame`. - -It addresses all these issues and even more. - -The syntax: -```js -let requestId = requestAnimationFrame(callback) -``` - -That schedules the `callback` function to run in the closest time when the browser wants to do animation. - -If we do changes in elements in `callback` then they will be grouped together with other `requestAnimationFrame` callbacks and with CSS animations. So there will be one geometry recalculation and repaint instead of many. - -The returned value `requestId` can be used to cancel the call: -```js -// cancel the scheduled execution of callback -cancelAnimationFrame(requestId); -``` - -The `callback` gets one argument -- the time passed from the beginning of the page load in microseconds. This time can also be obtained by calling [performance.now()](mdn:api/Performance/now). - -Usually `callback` runs very soon, unless the CPU is overloaded or the laptop battery is almost discharged, or there's another reason. - -The code below shows the time between first 20 runs for `requestAnimationFrame`. Usually it's 10-20ms: - -```html run height=40 refresh -<script> - let prev = performance.now(); - let times = 0; - - requestAnimationFrame(function measure(time) { - document.body.insertAdjacentHTML("beforeEnd", Math.floor(time - prev) + " "); - prev = time; - - if (times++ < 10) requestAnimationFrame(measure); - }) -</script> -``` - -## Structured animation - -Now we can make a more universal animation function based on `requestAnimationFrame`: - -```js -function animate({timing, draw, duration}) { - - let start = performance.now(); - - requestAnimationFrame(function animate(time) { - // timeFraction goes from 0 to 1 - let timeFraction = (time - start) / duration; - if (timeFraction > 1) timeFraction = 1; - - // calculate the current animation state - let progress = timing(timeFraction) - - draw(progress); // draw it - - if (timeFraction < 1) { - requestAnimationFrame(animate); - } - - }); -} -``` - -Function `animate` accepts 3 parameters that essentially describes the animation: - -`duration` -: Total time of animation. Like, `1000`. - -`timing(timeFraction)` -: Timing function, like CSS-property `transition-timing-function` that gets the fraction of time that passed (`0` at start, `1` at the end) and returns the animation completion (like `y` on the Bezier curve). - - For instance, a linear function means that the animation goes on uniformly with the same speed: - - ```js - function linear(timeFraction) { - return timeFraction; - } - ``` - - It's graph: - ![](linear.png) - - That's just like `transition-timing-function: linear`. There are more interesting variants shown below. - -`draw(progress)` -: The function that takes the animation completion state and draws it. The value `progress=0` denotes the beginning animation state, and `progress=1` -- the end state. - - This is that function that actually draws out the animation. - - It can move the element: - ```js - function draw(progress) { - train.style.left = progress + 'px'; - } - ``` - - ...Or do anything else, we can animate anything, in any way. - - -Let's animate the element `width` from `0` to `100%` using our function. - -Click on the element for the demo: - -[codetabs height=60 src="width"] - -The code for it: - -```js -animate({ - duration: 1000, - timing(timeFraction) { - return timeFraction; - }, - draw(progress) { - elem.style.width = progress * 100 + '%'; - } -}); -``` - -Unlike CSS animation, we can make any timing function and any drawing function here. The timing function is not limited by Bezier curves. And `draw` can go beyond properties, create new elements for like fireworks animation or something. - -## Timing functions - -We saw the simplest, linear timing function above. - -Let's see more of them. We'll try movement animations with different timing functions to see how they work. - -### Power of n - -If we want to speed up the animation, we can use `progress` in the power `n`. - -For instance, a parabolic curve: - -```js -function quad(timeFraction) { - return Math.pow(timeFraction, 2) -} -``` - -The graph: - -![](quad.png) - -See in action (click to activate): - -[iframe height=40 src="quad" link] - -...Or the cubic curve or event greater `n`. Increasing the power makes it speed up faster. - -Here's the graph for `progress` in the power `5`: - -![](quint.png) - -In action: - -[iframe height=40 src="quint" link] - -### The arc - -Function: - -```js -function circ(timeFraction) { - return 1 - Math.sin(Math.acos(timeFraction)); -} -``` - -The graph: - -![](circ.png) - -[iframe height=40 src="circ" link] - -### Back: bow shooting - -This function does the "bow shooting". First we "pull the bowstring", and then "shoot". - -Unlike previous functions, it depends on an additional parameter `x`, the "elasticity coefficient". The distance of "bowstring pulling" is defined by it. - -The code: - -```js -function back(x, timeFraction) { - return Math.pow(timeFraction, 2) * ((x + 1) * timeFraction - x) -} -``` - -**The graph for `x = 1.5`:** - -![](back.png) - -For animation we use it with a specific value of `x`. Example for `x = 1.5`: - -[iframe height=40 src="back" link] - -### Bounce - -Imagine we are dropping a ball. It falls down, then bounces back a few times and stops. - -The `bounce` function does the same, but in the reverse order: "bouncing" starts immediately. It uses few special coefficients for that: - -```js -function bounce(timeFraction) { - for (let a = 0, b = 1, result; 1; a += b, b /= 2) { - if (timeFraction >= (7 - 4 * a) / 11) { - return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2) - } - } -} -``` - -In action: - -[iframe height=40 src="bounce" link] - -### Elastic animation - -One more "elastic" function that accepts an additional parameter `x` for the "initial range". - -```js -function elastic(x, timeFraction) { - return Math.pow(2, 10 * (timeFraction - 1)) * Math.cos(20 * Math.PI * x / 3 * timeFraction) -} -``` - -**The graph for `x=1.5`:** -![](elastic.png) - -In action for `x=1.5`: - -[iframe height=40 src="elastic" link] - -## Reversal: ease* - -So we have a collection of timing functions. Their direct application is called "easeIn". - -Sometimes we need to show the animation in the reverse order. That's done with the "easeOut" transform. - -### easeOut - -In the "easeOut" mode the `timing` function is put into a wrapper `timingEaseOut`: - -```js -timingEaseOut(timeFraction) = 1 - timing(1 - timeFraction) -``` - -In other words, we have a "transform" function `makeEaseOut` that takes a "regular" timing function and returns the wrapper around it: - -```js -// accepts a timing function, returns the transformed variant -function makeEaseOut(timing) { - return function(timeFraction) { - return 1 - timing(1 - timeFraction); - } -} -``` - -For instance, we can take the `bounce` function described above and apply it: - -```js -let bounceEaseOut = makeEaseOut(bounce); -``` - -Then the bounce will be not in the beginning, but at the end of the animation. Looks even better: - -[codetabs src="bounce-easeout"] - -Here we can see how the transform changes the behavior of the function: - -![](bounce-inout.png) - -If there's an animation effect in the beginning, like bouncing -- it will be shown at the end. - -In the graph above the <span style="color:#EE6B47">regular bounce</span> has the red color, and the <span style="color:#62C0DC">easeOut bounce</span> is blue. - -- Regular bounce -- the object bounces at the bottom, then at the end sharply jumps to the top. -- After `easeOut` -- it first jumps to the top, then bounces there. - -### easeInOut - -We also can show the effect both in the beginning and the end of the animation. The transform is called "easeInOut". - -Given the timing function, we calculate the animation state like this: - -```js -if (timeFraction <= 0.5) { // first half of the animation - return timing(2 * timeFraction) / 2; -} else { // second half of the animation - return (2 - timing(2 * (1 - timeFraction))) / 2; -} -``` - -The wrapper code: - -```js -function makeEaseInOut(timing) { - return function(timeFraction) { - if (timeFraction < .5) - return timing(2 * timeFraction) / 2; - else - return (2 - timing(2 * (1 - timeFraction))) / 2; - } -} - -bounceEaseInOut = makeEaseInOut(bounce); -``` - -In action, `bounceEaseInOut`: - -[codetabs src="bounce-easeinout"] - -The "easeInOut" transform joins two graphs into one: `easeIn` (regular) for the first half of the animation and `easeOut` (reversed) -- for the second part. - -The effect is clearly seen if we compare the graphs of `easeIn`, `easeOut` and `easeInOut` of the `circ` timing function: - -![](circ-ease.png) - -- <span style="color:#EE6B47">Red</span> is the regular variantof `circ` (`easeIn`). -- <span style="color:#8DB173">Green</span> -- `easeOut`. -- <span style="color:#62C0DC">Blue</span> -- `easeInOut`. - -As we can see, the graph of the first half of the animation is the scaled down `easeIn`, and the second half is the scaled down `easeOut`. As a result, the animation starts and finishes with the same effect. - -## More interesting "draw" - -Instead of moving the element we can do something else. All we need is to write the write the proper `draw`. - -Here's the animated "bouncing" text typing: - -[codetabs src="text"] - -## Summary - -JavaScript animation should be implemented via `requestAnimationFrame`. That built-in method allows to setup a callback function to run when the browser will be preparing a repaint. Usually that's very soon, but the exact time depends on the browser. - -When a page is in the background, there are no repaints at all, so the callback won't run: the animation will be suspended and won't consume resources. That's great. - -Here's the helper `animate` function to setup most animations: - -```js -function animate({timing, draw, duration}) { - - let start = performance.now(); - - requestAnimationFrame(function animate(time) { - // timeFraction goes from 0 to 1 - let timeFraction = (time - start) / duration; - if (timeFraction > 1) timeFraction = 1; - - // calculate the current animation state - let progress = timing(timeFraction); - - draw(progress); // draw it - - if (timeFraction < 1) { - requestAnimationFrame(animate); - } - - }); -} -``` - -Options: - -- `duration` -- the total animation time in ms. -- `timing` -- the function to calculate animation progress. Gets a time fraction from 0 to 1, returns the animation progress, usually from 0 to 1. -- `draw` -- the function to draw the animation. - -Surely we could improve it, add more bells and whistles, but JavaScript animations are not applied on a daily basis. They are used to do something interesting and non-standard. So you'd want to add the features that you need when you need them. - -JavaScript animations can use any timing function. We covered a lot of examples and transformations to make them even more versatile. Unlike CSS, we are not limited to Bezier curves here. - -The same is about `draw`: we can animate anything, not just CSS properties. diff --git a/3-animation/3-js-animation/back.png b/3-animation/3-js-animation/back.png deleted file mode 100644 index c63bc9d958..0000000000 Binary files a/3-animation/3-js-animation/back.png and /dev/null differ diff --git a/3-animation/3-js-animation/back@2x.png b/3-animation/3-js-animation/back@2x.png deleted file mode 100644 index 794f162354..0000000000 Binary files a/3-animation/3-js-animation/back@2x.png and /dev/null differ diff --git a/3-animation/3-js-animation/bezier-linear.png b/3-animation/3-js-animation/bezier-linear.png deleted file mode 100644 index 795e4cdf4d..0000000000 Binary files a/3-animation/3-js-animation/bezier-linear.png and /dev/null differ diff --git a/3-animation/3-js-animation/bezier-linear@2x.png b/3-animation/3-js-animation/bezier-linear@2x.png deleted file mode 100644 index 95ec3cf913..0000000000 Binary files a/3-animation/3-js-animation/bezier-linear@2x.png and /dev/null differ diff --git a/3-animation/3-js-animation/bounce-inout.png b/3-animation/3-js-animation/bounce-inout.png deleted file mode 100644 index af1e90d762..0000000000 Binary files a/3-animation/3-js-animation/bounce-inout.png and /dev/null differ diff --git a/3-animation/3-js-animation/bounce-inout@2x.png b/3-animation/3-js-animation/bounce-inout@2x.png deleted file mode 100644 index 1c558f469c..0000000000 Binary files a/3-animation/3-js-animation/bounce-inout@2x.png and /dev/null differ diff --git a/3-animation/3-js-animation/circ-ease.png b/3-animation/3-js-animation/circ-ease.png deleted file mode 100644 index 657942f736..0000000000 Binary files a/3-animation/3-js-animation/circ-ease.png and /dev/null differ diff --git a/3-animation/3-js-animation/circ-ease@2x.png b/3-animation/3-js-animation/circ-ease@2x.png deleted file mode 100644 index 9f68a73f26..0000000000 Binary files a/3-animation/3-js-animation/circ-ease@2x.png and /dev/null differ diff --git a/3-animation/3-js-animation/circ.png b/3-animation/3-js-animation/circ.png deleted file mode 100644 index cdb43f7988..0000000000 Binary files a/3-animation/3-js-animation/circ.png and /dev/null differ diff --git a/3-animation/3-js-animation/circ@2x.png b/3-animation/3-js-animation/circ@2x.png deleted file mode 100644 index 7d1c7ced90..0000000000 Binary files a/3-animation/3-js-animation/circ@2x.png and /dev/null differ diff --git a/3-animation/3-js-animation/elastic.png b/3-animation/3-js-animation/elastic.png deleted file mode 100644 index f18ffbdca2..0000000000 Binary files a/3-animation/3-js-animation/elastic.png and /dev/null differ diff --git a/3-animation/3-js-animation/elastic@2x.png b/3-animation/3-js-animation/elastic@2x.png deleted file mode 100644 index 2dbd6d3cf5..0000000000 Binary files a/3-animation/3-js-animation/elastic@2x.png and /dev/null differ diff --git a/3-animation/3-js-animation/linear.png b/3-animation/3-js-animation/linear.png deleted file mode 100644 index 9d29ec1bd6..0000000000 Binary files a/3-animation/3-js-animation/linear.png and /dev/null differ diff --git a/3-animation/3-js-animation/linear@2x.png b/3-animation/3-js-animation/linear@2x.png deleted file mode 100644 index e10b7fef8d..0000000000 Binary files a/3-animation/3-js-animation/linear@2x.png and /dev/null differ diff --git a/3-animation/3-js-animation/quad.png b/3-animation/3-js-animation/quad.png deleted file mode 100644 index 670715a7f3..0000000000 Binary files a/3-animation/3-js-animation/quad.png and /dev/null differ diff --git a/3-animation/3-js-animation/quad@2x.png b/3-animation/3-js-animation/quad@2x.png deleted file mode 100644 index 9a3ca207bb..0000000000 Binary files a/3-animation/3-js-animation/quad@2x.png and /dev/null differ diff --git a/3-animation/3-js-animation/quint.png b/3-animation/3-js-animation/quint.png deleted file mode 100644 index 35c06319a4..0000000000 Binary files a/3-animation/3-js-animation/quint.png and /dev/null differ diff --git a/3-animation/3-js-animation/quint@2x.png b/3-animation/3-js-animation/quint@2x.png deleted file mode 100644 index d3477022aa..0000000000 Binary files a/3-animation/3-js-animation/quint@2x.png and /dev/null differ diff --git a/3-frames-and-windows/01-popup-windows/article.md b/3-frames-and-windows/01-popup-windows/article.md new file mode 100644 index 0000000000..1d16e32afb --- /dev/null +++ b/3-frames-and-windows/01-popup-windows/article.md @@ -0,0 +1,259 @@ +# ポップアップとウィンドウメソッド + +ポップアップウィンドウは、利用者に追加のコンテンツを見せるための最も古い方法の1つです。 + +基本的には次のように実行するだけです: +```js +window.open('http://javascript.info/') +``` + +... すると、指定された URL で新しいウィンドウが開きます。ほとんどのモダンブラウザは、別ウィンドウではなく新しいタブとして開くよう設定されています。 + +ポップアップはとても古くから存在します。当初の考えは、メインのウィンドウを閉じることなく別のコンテンツを表示することでした。現時点では、それをするための他の方法があります: [fetch](info:fetch) を使うことでコンテンツを動的に読み込むことができ、それを動的に生成された `<div>` の中で表示することができます。ですから、ポップアップは私達が普段使用するものではありません。 + +さらにポップアップは、複数のウィンドウを同時には表示しないモバイルデバイスでは手際を要します。 + +それでも、ポップアップがいまだに使われるタスクが存在します。例えば OAuth 認証(Google や Facebook などへのログイン)。なぜなら: + +1. ポップアップは独立した JavaScript 環境を持つウィンドウです。ですから、第三者の信頼されていないサイトから開くポップアップは安全です。 +2. ポップアップを開くことは非常に簡単です。 +3. ポップアップはナビゲート(URLの変更)可能で、ポップアップを開いたウィンドウにメッセージを送ることができます。 + +## ポップアップブロック + +過去、悪意のあるサイトはポップアップを大いに乱用しました。悪意のあるページは広告を含むウィンドウを何度も開く事ができました。そのため、現在多くのブラウザはポップアップをブロックし、ユーザを守ろうとしています。 + +**ほとんどのブラウザは、`onclick` などユーザがトリガーしたイベントハンドラ外から呼ばれた場合には、ポップアップをブロックします。** + +これについて考える場合、少し注意が必要です。もしコードが直接 `onclick` 内にあればそれは簡単です。しかし、ポップアップは `setTimeout` で開くでしょうか? + +例えば: +```js +// ポップアップはブロックされます +window.open('https://javascript.info'); + +// ポップアップは許可されます +button.onclick = () => { + window.open('https://javascript.info'); +}; +``` + +このように、ユーザは望まないポップアップからある程度は守られていますが、その機能は完全には無効にされていません。 + +## window.open + +ポップアップを開く構文は次の通りです: `window.open(url, name, params)`: + +url +: 新しいウィンドウでロードする URL + +name +: 新しいウィンドウの名前。各ウィンドウは `window.name` を持っており、ここでポップアップに使うウィンドウを指定することができます。すでに同じ名前のウィンドウがあった場合、そこで指定された URL が開きます。なければ新しいウィンドウが開きます。 + +params +: 新しいウィンドウの設定文字列。カンマで区切られた設定を含みます。params の中にスペースを入れてはいけません。例: `width:200,height=100`. + +`params` の設定: + +- ポジション: + - `left/top` (数値) -- 画面上のウィンドウの左上隅の座標。新しいウィンドウを画面外に配置することはできない、という制限があります。 + - `width/height` (数値) -- 新しいウィンドウの width と height 。 最小の width/height の制限があるので、, 見えないウィンドウを作成することはできません。 +- ウィンドウの機能: + - `menubar` (yes/no) -- 新しいウィンドウで、ブラウザのメニューを表示します/非表示にします。 + - `toolbar` (yes/no) -- 新しいウィンドウで、ブラウザナビゲーション(戻る/進む/更新など)を表示します/非表示にします。 + - `location` (yes/no) -- 新しいウィンドウで、URL フィールドを表示します/非表示にします。FF と IE はデフォルトでは隠すことは許可されていません。 + - `status` (yes/no) -- ステータスバーを表示します/非表示にします。ほとんどのブラウザは強制的に表示させます。 + - `resizable` (yes/no) -- 新しいウィンドウのリサイズを無効にします。非推奨です。 + - `scrollbars` (yes/no) -- 新しいウィンドウのスクロールバーを無効にします。非推奨です。 + + +あまりサポートされていないブラウザ固有の機能も数多くありますが、通常は使用されていません。例については、<a href="https://developer.mozilla.org/en/DOM/window.open">MDN の window.open</a> を確認してみてください。 + +## 例: 最小限のウィンドウ + +ブラウザがどの機能の無効化を許容するか、最小セットの機能でウィンドウを開いてみましょう。: + +```js run +let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no, +width=0,height=0,left=-1000,top=-1000`; + +open('/', 'test', params); +``` + +ここでは、ほとんどの "ウィンドウの機能 は無効にされ、ウィンドウは画面外に配置されています。実行して実際に何が起きるのかを見てください。ほとんどのブラウザはゼロ値の `width/height` や画面外の `left/top` といったおかしなものを "直します"。例えば、Chrome はフルスクリーンになるよう、画面幅/高さでウィンドウを開きます。 + +通常の配置オプションと妥当な `width`, `height`, `left`, `top` 座標を追加しましょう。: + +```js run +let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no, +width=600,height=300,left=100,top=100`; + +open('/', 'test', params); +``` + +ほとんどのブラウザは要求に従って上記の例を表示します。 + +設定が省略された際のルール: + +- `open` 呼び出しで 3つ目の引数がない場合、もしくはそれが空の場合、デフォルトのウィンドウパラメータが使われます。 +- params の文字列はあるが、一部の機能の yes/no が省略されている場合、省略された機能はブラウザで許可されていれば無効になります。そのため、params を指定する場合には、明示的に必要なすべての機能に yes を設定してください。 +- params に `left/top` がない場合、ブラウザは最後に開いたウィンドウの近くに新しいウィンドウを開こうとします。 +- `width/height` がない場合、新しいウィンドウは最後に開いたウィンドウと同じサイズになります。 + +## ポップアップにアクセスする + +`open` 呼び出しは、新しいウィンドウへの参照を返します。それはプロパティを操作したり、位置を変えたりといったことをするのに利用できます。 + +この例では、ポップアップの内容を JavaScript で生成します: + +```js +let newWin = window.open("about:blank", "hello", "width=200,height=200"); + +newWin.document.write("Hello, world!"); +``` + +またこちらでは、コンテンツをロード後に変更します: + +```js run +let newWindow = open('/', 'example', 'width=300,height=300') +newWindow.focus(); + +alert(newWindow.location.href); // (*) about:blank, 読み込みはまだ始まっていません + +newWindow.onload = function() { + let html = `<div style="font-size:30px">Welcome!</div>`; +*!* + newWindow.document.body.insertAdjacentHTML('afterbegin', html); +*/!* +}; +``` + +注意してください: `window.open` の直後は、新しいウィンドウはまだ読み込まれていません。これは `(*)` の行にある `alert` で示されています。ですから、変更するために `onload` を待っています。 `newWin.document` 用に `DOMContentLoaded` ハンドラを使うこともできます。 + +```warn header="同一生成元ポリシー" +ウィンドウは、同じ元(同じプロトコル://ドメイン:ポート)から生成された場合のみ、互いのコンテンツに自由にアクセスすることができます。 + +そうでなければ、例えばメインウィンドウが `site.com` から生成され、ポップアップが `gmail.com` から生成された場合、ユーザの安全性のため不可能となります。詳しくは、 <info:cross-window-communication> の章を確認してください。 +``` + +## 開いた元(opener)のウィンドウにアクセスする + +ポップアップは `window.opener` 参照を用いることでも "opener" ウィンドウにアクセスできます。ポップアップ以外のすべてのウィンドウの場合、それは `null` です。 + +以下のコードを実行すると、 opener ウィンドウ(現在のウィンドウ)の内容が "Test" に置き換わります: + +```js run +let newWin = window.open("about:blank", "hello", "width=200,height=200"); + +newWin.document.write( + "<script>window.opener.document.body.innerHTML = 'Test'<\/script>" +); +``` + +ですから、ウィンドウ間の接続は双方向です: メインウィンドウとポップアップは互いへの参照を持っています。 + +## ポップアップを閉じる + +ウィンドウを閉じるには: `win.close()`。 + +ウィンドウが閉じられたかを確認するには: `win.closed`。 + +技術的には、`close()` メソッドはどの `window` でも利用可能ですが、`window` が `window.open()` で生成されたものでない場合、`window.close()` はほとんどのブラウザで無視されます。ですからこれはポップアップでのみ機能します。 + +`closed` 属性は、ウィンドウが閉じられた場合 `true` です。これはポップアップ(あるいはメインウィンドウ)がまだ開かれているかどうかを確認するのに便利です。ユーザはいつでもそれを閉じることができ、私達のコードではその可能性を考慮に入れるべきです。 + +このコードはウィンドウを開いた後、閉じます。: + +```js run +let newWindow = open('/', 'example', 'width=300,height=300') + +newWindow.onload = function() { + newWindow.close(); + alert(newWindow.closed); // true +}; +``` + +## 移動とリサイズ + +ウィンドウを動かす/リサイズするメソッドがあります: + +`win.moveBy(x,y)` +: ウィンドウを現在の位置から相対的に `x` ピクセル右に、 `y` ピクセル下に動かします。負の値は許可されます(左/上に動きます)。 + +`win.moveTo(x,y)` +: ウィンドウをスクリーン上の座標 `(x,y)` に動かします。 + +`win.resizeBy(width,height)` +: ウィンドウを現在の大きさから相対的に与えられた `width/height` だけリサイズします。負の値は許可されます。 + +`win.resizeTo(width,height)` +: ウィンドウを与えられた大きさにリサイズします。 + +`window.onresize` イベントもあります。 + +```warn header="ポップアップのみ" +乱用を防ぐため、ブラウザは通常これらのメソッドをブロックします。これらは私達が開いた、追加のタブのないポップアップに対してのみ確実に機能します。 +``` + +```warn header="最小化/最大化はありません" +JavaScript にはウィンドウを最小化あるいは最大化する方法がありません。これらの OS レベルの機能はフロントエンド開発者から隠されています。 + +移動/リサイズのメソッドは最大化/最小化されたウィンドウに対しては効きません。 +``` + +## ウィンドウのスクロール + +私達は既に<info:size-and-scroll-window>の章で、ウィンドウをスクロールすることについて述べてきました。 + +`win.scrollBy(x,y)` +: ウィンドウを現在のスクロールから相対的に `x` ピクセル右に、 `y` ピクセル下にスクロールします。負の値は許可されます。 + +`win.scrollTo(x,y)` +: ウィンドウを与えられた座標 `(x,y)` にスクロールします。 + +`elem.scrollIntoView(top = true)` +: ウィンドウを、 `elem` が上部に(デフォルト)、あるいは `elem.scrollIntoView(false)` の場合は下部に表示されるようにウィンドウをスクロールします。 + +`window.onscroll` イベントもあります。 + +## ポップアップへの focus/blur + +理論的には、ウィンドウにフォーカスを当てる/外す `window.focus()` と `window.blur()` メソッドがあります。また、ウィンドウにフォーカスしたり、訪問者が別の場所へ切り替えた瞬間を捉える `focus/blur` イベントもあります。 + +けれども、実際これらはかなり制限されています。なぜなら過去に悪意のあるページはこれらを乱用したからです。 + +例えば、次のコードを見てください。: + +```js run +window.onblur = () => window.focus(); +``` + +利用者がウィンドウから出ようとすると(`window.blur`)、このコードはフォーカスを戻します。この意図は、利用者を `window` 内に "ロック" することです。 + +そのため、ブラウザはこのようなコードを禁止し、広告や悪意のあるページからユーザを守るために多くの制限を導入しなければなりませんでした。これらはブラウザによります。 + +例えば、モバイルブラウザは通常、 `window.focus()` を完全に無視します。また、ポップアップが新しいウィンドウではなく別のタブで開いた場合、フォーカスは動作しません。 + +それでも、そのような呼び出しが機能し、役に立つようなユースケースもあります。 + +例: + +- ポップアップを開く際、`newWindow.focus()` を行うのは良いアイデアかもしれません。OS/ブラウザの組み合わせによっては、ユーザが新しいウィンドウにいることを保証します。 +- 訪問者が実際にいつ我々の web アプリを利用したかを追跡したい場合、`window.onfocus/onblur` が使えます。これにより、ページ内でのアクティビティやアニメーションなどを一時停止/再開することができます。しかし、`blur` イベントは訪問者がウィンドウを切り替えたことを意味しますが、それでも監視できる可能性があることに留意してください。ウィンドウはバックグラウンドにありますが、まだ表示されている可能性があります。 + +## サマリ + +ポップアップウィンドウはほとんど使われません。なぜなら代替方法があるからです: 情報をページ内あるいは iframe 内で読み込みます。 + +もしポップアップを開くつもりならば、それをユーザに伝えることをおすすめします。リンクやボタンの近くに "ウィンドウを開く" アイコンがあれば、訪問者はフォーカスの遷移を念頭に置くことができますし、両方のウィンドウを気に留めておくことができます。 + +- ポップアップは `open(url, name, params)` 呼び出しで開くことができます。これは新しく開かれたウィンドウへの参照を返します。 +- ブラウザは、ユーザアクション外からの `open` 呼び出しをブロックします。通常、通知が表示されるので、利用者はそれを許可することができます。 +- ブラウザはデフォルトで、新しいタブを開きます。しかし大きさが渡されている場合、ポップアップウィンドウを開きます。 +- ポップアップは `window.opener` プロパティを利用して、開いた元のウィンドウにアクセスできます。 +- メインウィンドウとポップアップが同じ生成元から来たものである場合、お互いを自由に読み書きすることができます。そうでない場合、お互いの location を変更し、[メッセージを使用して](info:cross-window-communication)やり取りすることができます(この後説明があります)。 + +ポップアップを閉じるには、`close()` 呼び出しを使用します。また、ユーザもそれを閉じることができます。その後、 `window.closed` は `true` です。 + +- メソッド `focus()` と `blur()` でウィンドウへフォーカスしたり外したりできますが、いつも機能するとは限りません。 +- イベント `focus` と `blur` によりウィンドウの内外への切り替えを追跡することができます。しかし、ウィンドウは `blur` の後、バックグラウンド状態でも見えるかもしれないことに留意してください。 diff --git a/3-frames-and-windows/03-cross-window-communication/article.md b/3-frames-and-windows/03-cross-window-communication/article.md new file mode 100644 index 0000000000..622eeab545 --- /dev/null +++ b/3-frames-and-windows/03-cross-window-communication/article.md @@ -0,0 +1,366 @@ +# ウィンドウを跨いだやり取り + +"同一オリジン" (同一サイト) ポリシーは、ウィンドウとフレームのアクセスを互いに制限します。 + +2つのウィンドウが開いているとします: 1つは `john-smith.com`、もう1つは `gmail.com` です。この場合、`john-smith.com` がメールを読むようなスクリプトは望まないでしょう。 + +[cut] + +## 同一オリジン(Same Origin) + +同じプロトコル、ドメインとポートを持つ場合、2つの URL は "同一オリジン" 言われます。 + +これらの URL はすべて同じオリジンです: + +- `http://site.com` +- `http://site.com/` +- `http://site.com/my/page.html` + +これらは違います: + +- <code>http://<b>www.</b>site.com</code> (別のドメイン: `www.` のため) +- <code>http://<b>site.org</b></code> (別のドメイン: `.org` のため) +- <code><b>https://</b>site.com</code> (別のプロトコル: `https`) +- <code>http://site.com:<b>8080</b></code> (別のポート: `8080`) + +"同一オリジン" ポリシーは次のようになります: + +- 別のウィンドウへの参照があり(e.g. `window.open` によって作られたポップアップ、あるいは `<iframe>` 内のウィンドウ)、そのウィンドウが同一オリジンから来ている場合は、そのウィンドウへのフルアクセスを持ちます。 +- そうではなく、別のオリジンから来たものである場合、そのウィンドウの内容にアクセスすることはできません。: 変数、ドキュメント、その他すべて。唯一の例外は `location` です: それは変えることができます(結果、ユーザをリダイレクトします)。しかし、location を *読む* ことはできません(したがって、ユーザが今どこにいるのかを知ることはできず、情報が漏れることはありません)。 + +それでは、いくつか例を見てみましょう。まず、同じオリジンから来て、"同一オリジン" ポリシーに衝突しないページを見ます。その後、"同一オリジン" ポリシーを回避することができる、ウィンドウ間のメッセージングについて説明します。 + +````warn header="サブドメインは同一オリジンの場合があります" +"同一オリジン" ポリシーには、小さな例外があります。 + +ウィンドウが同じ第2レベルのドメインを共有している場合、例えば `john.site.com`, `peter.site.com` と `site.com` (これらの共通の第2レベルのドメインは `site.com` です)、これらは "同一オリジン" から来ているものとして扱う事ができます。 + +それを機能させるためには、このようなすべてのページ(`site.com` からのものも含む)は、次のコードを実行する必要があります: + +```js +document.domain = 'site.com'; +``` + +これだけです。これで制限なしにやり取りすることができます。繰り返しますが、これは同じ第2レベルのドメインをもつページでのみ可能です。 +```` + +## iframe のコンテンツにアクセスする + +最初の例では iframe を説明します。`<iframe>` は二面のある獣です。それは `<script>` あるいは `<img>` と同じような単なるタグである一方、ウィンドウ内のウィンドウです。 + +埋め込みのウィンドウは別の `document` と `window` オブジェクトを持ちます。 + +プロパティを使って、それらにアクセスできます。: + +- `iframe.contentWindow` は `<iframe>` 内のウィンドウへの参照です。 +- `iframe.contentDocument` は `<iframe>` 内のドキュメントへの参照です。 + +埋め込みのウィンドウへアクセスする際、ブラウザは iframe が同一オリジンかをチェックします。もし同一でない場合、アクセスは拒否されます(上で述べた例外を除く)。 + +例えば、これは別のオリジンの `<iframe>` です。: + +```html run +<iframe src="https://example.com" id="iframe"></iframe> + +<script> + iframe.onload = function() { + // 内部のウィンドウへの参照を取得できます + let iframeWindow = iframe.contentWindow; + + try { + // ...が、その中のドキュメントは取得できません + let doc = iframe.contentDocument; + } catch(e) { + alert(e); // セキュリティエラー(別オリジン) + } + + // その中のページの URL を見ることもできません + try { + alert(iframe.contentWindow.location); + } catch(e) { + alert(e); // セキュリティエラー + } + + // ...しかし、変更(し、iframe 内になにかをロード)することはできます! + iframe.contentWindow.location = '/'; // 動作します + + iframe.onload = null; // 一度だけ実行させるためにハンドラをクリア + }; +</script> +``` + +上のコードは、以下を除く操作に対してエラーを表示します。: + +- 内部のウィンドウ `iframe.contentWindow` への参照を取得する +- その `location` を変更する + +```smart header="`iframe.onload` vs `iframe.contentWindow.onload`" +`iframe.onload` イベントは実際には `iframe.contentWindow.onload` と同じです。これは埋め込みウィンドウがすべてのリソース含め完全に読み込まれた時にトリガーされます。 + +...しかし、`iframe.onload` は常に利用可能な一方、`iframe.contentWindow.onload` は同一オリジンである必要があります。 +``` + +そして、これは同一オリジンの例です。埋め込みウィンドウでなんでもできます。: + +```html run +<iframe src="/" id="iframe"></iframe> + +<script> + iframe.onload = function() { + // なんでもできます + iframe.contentDocument.body.prepend("Hello, world!"); + }; +</script> +``` + +### iframe がロードされるまで待ってください + +iframe が作成されると、すぐにドキュメントを持ちます。しかし、そのドキュメントは最終的にそこにロードされるものとは異なります! + +ここで見てください: + +```html run +<iframe src="/" id="iframe"></iframe> + +<script> + let oldDoc = iframe.contentDocument; + iframe.onload = function() { + let newDoc = iframe.contentDocument; +*!* + // ロードされたドキュメントは初期のものとは同じではありません! + alert(oldDoc == newDoc); // false +*/!* + }; +</script> +``` + +これは実際に、開発者の間でよく知られた落とし穴です。それは *間違ったドキュメント* なので、すぐにドキュメントを使った処理をするべきではありません。もしそこに任意のイベントハンドラを設定しても無視されます。 + +...しかし、`onload` イベントはすべてのリソースを含む iframe 全体がロードされたときにトリガーされます。仮により早く、埋め込みドキュメントの `DOMContentLoaded` でなにかしたい場合どうすればよいでしょうか? + +iframe が別のオリジンから来ている場合は不可能です。しかし、同一オリジンの場合は、次のように新しいドキュメントが現れる瞬間を捉えて、必要なハンドラの設定を試みることができます。: + +```html run +<iframe src="/" id="iframe"></iframe> + +<script> + let oldDoc = iframe.contentDocument; + + // ドキュメントが新しいものか 100ms 毎にチェック + let timer = setInterval(() => { + if (iframe.contentDocument == oldDoc) return; + + // 新しいドキュメントなので、ハンドラをセットします + iframe.contentDocument.addEventListener('DOMContentLoaded', () => { + iframe.contentDocument.body.prepend('Hello, world!'); + }); + + clearInterval(timer); // もう必要ないので setInterval をクリアします + }, 100); +</script> +``` + +より良い方法を知っていたらコメントで教えてください。 + +## window.frames + +`<iframe>` のウィンドウオブジェクトを取得する別の方法は、名前付けされたコレクション `window.frames` から取得することです。 + +- 数値で: `window.frames[0]` -- ドキュメントの1つ目のフレームのウィンドウオブジェクトです。 +- 名前で: `window.frames.iframeName` -- `name="iframeName"` を持つフレームのウィンドウオブジェクトです。 + +例: + +```html run +<iframe src="/" style="height:80px" name="win" id="iframe"></iframe> + +<script> + alert(iframe.contentWindow == frames[0]); // true + alert(iframe.contentWindow == frames.win); // true +</script> +``` + +iframe は内側に別の iframe を持つ場合があります。対応する `window` オブジェクトを階層を形成します。 + +ナビゲーションリンクは次のようになります: + +- `window.frames` -- "子" のウィンドウのコレクション(ネストされたフレーム用) +- `window.parent` -- "親" (外部の)ウィンドウへの参照 +- `window.top` -- 一番上のウィンドウへの参照 + +例: + +```js run +window.frames[0].parent === window; // true +``` + +現在のドキュメントがフレームの中で開かれているかどうかを確認するのに、`top` プロパティが使えます。: + +```js run +if (window == top) { // current window == window.top? + alert('The script is in the topmost window, not in a frame'); +} else { + alert('The script runs in a frame!'); +} +``` + +## The sandbox attribute + +`sandbox` 属性は、信頼できないコードが実行されるのを防ぐため、`<iframe>` 内の特定のアクションを除外することができます。それらを別のオリジンからくるものとして扱うことによって、または他の制限を適用することによって、iframeを "サンドボックス化" します。 + +デフォルトでは、`<iframe sandbox src="...">` に対しては、"デフォルトセット" の制限が iframe に適用されます。しかし、`<iframe sandbox="allow-forms allow-popups">` のように、属性の値に "除外された" 制限のリストをスペース区切りで指定することもできます。この場合、列挙されている制限は適用されません。 + +つまり、空の `"sandbox"` 属性は最も厳しい制限にすることを意味し、そこから除外したいもののリストをスペース区切りで指定することができます。 + +ここは制限の一覧です: + +`allow-same-origin` +: デフォルトでは、`"sandbox"` は iframe に対し、"異なるオリジン" ポリシーを矯正します。つまり、ブラウザはたとえ iframe の `src` が同じサイトを指していたとしても、`iframe` を別のオリジンから来たものとして扱います。スクリプトに対するすべての暗黙の制限を持ちます。このオプションはこの機能を削除します。 + +`allow-top-navigation` +: `iframe` が `parent.location` を変更するのを許可します。 + +`allow-forms` +: `iframe` からフォームを送信するのを許可します。 + +`allow-scripts` +: `iframe` からスクリプトを実行するのを許可します。 + +`allow-popups` +: `iframe` から `window.open` するのを許可します。 + +その他については、[マニュアル](mdn:/HTML/Element/iframe) を参照してください。 + +下の例は、制限のデフォルトセットが適用された、サンドボックス化された iframe のデモです: `<iframe sandbox src="...">`。そこんはいくつかの JavaScript とフォームがあります。 + +何も動作しないことに注目してください。デフォルトセットは本当に厳しいです。: + +[codetabs src="sandbox" height=140] + + +```smart +`"sandbox"` 属性の目的は、制限を *追加* することだけです。それらを削除することはできません。特に、iframe が別オリジンから来たときに、同一オリジン制限を緩めることはできません。 +``` + +## ウィンドウを跨いだメッセージング + +`postMessage` インタフェースはどのオリジンから来ていたとしても、ウィンドウ同士がやり取りするのを可能にします。 + +したがって、これは "同一オリジン" ポリシーの回避策です。これは `john-smith.com` からのウィンドウが `gmail.com` とやり取りし、情報を交換することを可能にしますが、両者が合意し、対応する JavaScript 関数を呼び出したときだけです。これは利用者にとっては安全です。 + +インタフェースは2つのパートがあります: + +### postMessage + +メッセージを送りたいウィンドウは、受け取るウィンドウの [postMessage](mdn:api/Window.postMessage)] メソッドを呼び出します。つまり、`win` にメッセージを送りたい場合、`win.postMessage(data, targetOrigin)` を呼び出す必要があります。 + +引数: + +`data` +: 送るデータ。任意のオブジェクトが指定可能で、データは "structured cloning algorithm" を利用して複製されます。IE は文字列のみをサポートしているので、IEをサポートする場合は、複雑なオブジェクトには `JSON.stringify` が必要です。 + +`targetOrigin` +: 指定されたオリジンのウィンドウだけがメッセージを受け取るように、ターゲットウィンドウのオリジンを指定します。 + +`targetOrigin` は安全対策です。思い出してください、ターゲットウィンドウが別のオリジンから来た場合、その `location` を読むことはできません。そのため、今どのサイトが意図したウィンドウで開かれているかを判断することはできません。 + +`targetOrigin` を指定すると、ウィンドウがまだそのサイトを表示している場合にのみウィンドウがデータを受け取るようになります。機密性が高いデータの場合に適しています。 + +例えば、ここでは `win` は、オリジン `http://example.com` からのドキュメントを持っている場合にのみ、メッセージを受け取ります。 + +```html no-beautify +<iframe src="http://example.com" name="example"> + +<script> + let win = window.frames.example; + + win.postMessage("message", "http://example.com"); +</script> +``` + +チェックしたくない場合は、`targetOrigin` に `*` を設定します。 + +```html no-beautify +<iframe src="http://example.com" name="example"> + +<script> + let win = window.frames.example; + +*!* + win.postMessage("message", "*"); +*/!* +</script> +``` + + +### onmessage + +メッセージを受け取るためには、ターゲットウィンドウは `message` イベントのハンドラが必要です。これは `postMessage` が呼び出され(そして `targetOrigin` チェックが成功した)ときに実行されます。 + +イベントオブジェクトは特別なプロパティを持っています: + +`data` +: `postMessage` からのデータ。 + +`origin` +: 送信側のオリジン。例えば `http://javascript.info`。 + +`source` +: 送信側のウィンドウへの参照。必要ならすぐに `postMessage` を返すことができます。 + +ハンドラを割り当てるには、`addEventListener` を使う必要があります。短縮構文 `window.onmessage` は動作しません。 + +例です: + +```js +window.addEventListener("message", function(event) { + if (event.origin != 'http://javascript.info') { + // 未知のドメインからの場合は無視しましょう + return; + } + + alert( "received: " + event.data ); +}); +``` + +完全な例です: + +[codetabs src="postmessage" height=120] + +```smart header="遅延はありません" +`postMessage` と `message` イベントの間には遅延はまったくありません。それらは同期的に発生し、`setTimeout(...,0)` よりも高速です。 +``` + +## サマリ + +メソッドを呼びだし、別ウィンドウのコンテンツにアクセスするには、最初にその参照が必要です。 + +ポップアップの場合、2つのプロパティがあります: +- `window.open` -- 新しいウィンドウを開き、そこへの参照を返します。 +- `window.opener` -- ポップアップから見た、ポップアップを開いたウィンドウへの参照です。 + +iframe の場合、次のようにして親/子のウィンドウにアクセスできます: +- `window.frames` -- ネストされたウィンドウオブジェクトの集合です。 +- `window.parent`, `window.top` や親や最上位のウィンドウへの参照です。 +- `iframe.contentWindow` は `<iframe>` タグ内のウィンドウです。 + +もしウィンドウが同一オリジンを共有している場合(ホスト、ポート、プロトコル)、ウィンドウは互いになんでもできます。 + +そうでない場合、できることは次のものだけです: +- 別ウィンドウの location の変更(書き込みのみのアクセス)。 +- そこへのメッセージの送信。 + +除外については、次の通りです: +- 同じ第2階層のドメインを共有しているウィンドウ: `a.site.com` と `b.site.com`。そして、両方に `document.domain='site.com` を設定することで、それらを "同一オリジン" の状態にします。 +- iframe が `sandbox` 属性を持っている場合、属性値に `allow-same-origin` が指定されていない限り、強制的に "異なるオリジン" の状態に置かれます。これは同一サイトからの iframe 内で信頼されていないコードを実行するのに使われます。 + +`postMessage` インタフェースで、2つのウィンドウ間でセキュリティチェックを含むやり取りが可能です。 + +1. 送信側は `targetWin.postMessage(data, targetOrigin)` を呼び出します。 +2. `targetOrigin` が `'*'` でない場合、ブラウザはウィンドウ `targetWin` が `targetWin` サイトからの URL かどうかをチェックします。 +3. その場合、`targetWin` は特別なプロパティを持つ `message` イベントをトリガーします: + - `origin` -- 送信側のウィンドウのオリジン(`http://my.site.com` など) + - `source` -- 送信側のウィンドウへの参照 + - `data` -- データ。任意のオブジェクト。IEだけは文字列のみをサポートします。 + + ターゲットウィンドウ内でこのイベントのハンドラを設定するには、`addEventListener` を使用する必要があります。 diff --git a/4-frames-and-windows/03-cross-window-communication/postmessage.view/iframe.html b/3-frames-and-windows/03-cross-window-communication/postmessage.view/iframe.html similarity index 100% rename from 4-frames-and-windows/03-cross-window-communication/postmessage.view/iframe.html rename to 3-frames-and-windows/03-cross-window-communication/postmessage.view/iframe.html diff --git a/4-frames-and-windows/03-cross-window-communication/postmessage.view/index.html b/3-frames-and-windows/03-cross-window-communication/postmessage.view/index.html similarity index 100% rename from 4-frames-and-windows/03-cross-window-communication/postmessage.view/index.html rename to 3-frames-and-windows/03-cross-window-communication/postmessage.view/index.html diff --git a/4-frames-and-windows/03-cross-window-communication/sandbox.view/index.html b/3-frames-and-windows/03-cross-window-communication/sandbox.view/index.html similarity index 100% rename from 4-frames-and-windows/03-cross-window-communication/sandbox.view/index.html rename to 3-frames-and-windows/03-cross-window-communication/sandbox.view/index.html diff --git a/4-frames-and-windows/03-cross-window-communication/sandbox.view/sandboxed.html b/3-frames-and-windows/03-cross-window-communication/sandbox.view/sandboxed.html similarity index 100% rename from 4-frames-and-windows/03-cross-window-communication/sandbox.view/sandboxed.html rename to 3-frames-and-windows/03-cross-window-communication/sandbox.view/sandboxed.html diff --git a/3-frames-and-windows/06-clickjacking/article.md b/3-frames-and-windows/06-clickjacking/article.md new file mode 100644 index 0000000000..102b1015d4 --- /dev/null +++ b/3-frames-and-windows/06-clickjacking/article.md @@ -0,0 +1,202 @@ +# クリックジャッキング + +"クリックジャッキング" 攻撃は、*訪問者の代わりに*、 悪意のあるページが "被害を受けたサイト" 上でクリックすることを可能とするものです。 + +Twitter, Facebook, Paypal やその他を含む多くのサイトはこの方法でハッキングされました。もちろん、今は対処済みです。 + +## 考え方 + +この考え方はとても単純です。 + +これは、Facebookで行われたクリックジャッキングです: + +1. 訪問者が悪意のあるページに誘い出されます。方法はここでは関係ありません。 +2. そのページは無害に見えるリンクを持っています("今すぐお金持ちになる" や "ここをクリックしてください、とても面白いよ" など)。 +3. そのリンクの上に、例えば "いいね" ボタンがそのリンクの真上にくるように、悪意のあるページが facebook.com の `src` を利用した透明な `<iframe>` を配置します。通常これは `z-index` で行われます。 +4. リンクをクリックしようとしたとき、実際には訪問者はそのボタンをクリックすることになります。 + +## デモ + +悪意のあるページがどのように見えるかのデモです。より明白にするために、 `<iframe>` は半透明にしています(実際には、悪意のあるページは完全に透明です)。: + +```html run height=120 no-beautify +<style> +iframe { /* 被害サイト からの iframe */ + width: 400px; + height: 100px; + position: absolute; + top:0; left:-20px; +*!* + opacity: 0.5; /* 実際には opacity:0 です */ +*/!* + z-index: 1; +} +</style> + +<div>Click to get rich now:</div> + +<!-- 被害サイトの url --> +*!* +<iframe src="/clickjacking/facebook.html"></iframe> + +<button>Click here!</button> +*/!* + +<div>...And you're cool (I'm a cool hacker actually)!</div> +``` + +攻撃の完全がデモがこちらです: + +[codetabs src="clickjacking-visible" height=160] + +ここでは 半透明な `<iframe src="facebook.html">` があり、この例では、"Click here!" と言うボタンの上にあるのが分かります。ボタンをクリックすると、実際には iframe をクリックしますが、iframe は透明なため、ユーザには見えません。 + +結果、訪問者が Facebook で認証済みの場合 (通常は "ログイン情報を覚えておく" が有効)、"いいね" がされます。Twitter では "フォロー" ボタンかもしれません。 + +次は、同じ例ですが、現実により近く、 `<iframe>` が `opacity:0` の場合です: + +[codetabs src="clickjacking" height=160] + +攻撃するために必要なことは、ボタンがリンクの真上にくるよう、`<iframe>` を悪意のあるページに配置することだけです。それは通常 CSS で可能です。 + +```smart header="クリックジャッキングはクリックに対するものであり、キーボードは含まれません" +この攻撃はマウス操作にのみ影響します。 + +技術的には、もしハッキングするテキストフィールドがある場合、テキストフィールドが重なるように iframe を配置することはできます。なので、訪問者がページに表示されている入力フィールドにフォーカスを当てようとしたとき、実際には iframe 内の入力フィールドにフォーカスします。 + +しかし、その後問題があります。iframe は見えないため、訪問者の入力したものは画面上に見えません。 + +画面上に入力した文字が表示されない場合、通常は入力をやめます。 +``` + +## 伝統的な防御策(弱い) + +最も古い防御策は、フレーム内でページを開くことを禁止する JavaScript です(いわゆる "フレームバスティング(framebusting)")。 + +このようになります: + +```js +if (top != window) { + top.location = window.location; +} +``` + +つまり: window がトップにないことがわかった場合、自身を自動的にトップにします。 + +ただ、これをハックする方法はたくさんあるため、信頼できる防御策ではありません。いくつか取り上げましょう。 + +### トップナビゲーションをブロックする + +[beforeunload](info:onload-ondomcontentloaded#window.onbeforeunload) イベントで、`top.location` を変更することにより引き起こされる遷移をブロックすることができます。 + +トップページ(ハッカーに属する)はそれにハンドラをセットし、`iframe` が `top.location` を変更しようとすると、訪問者はここを去りたいかを尋ねるメッセージを受け取ります。 + +このように: +```js +window.onbeforeunload = function() { + window.onbeforeunload = null; + return "Want to leave without learning all the secrets (he-he)?"; +}; +``` + +ほとんどの場合、訪問者は否定的な回答(ページを去らない)でしょう。なぜなら、彼らはiframe の存在は知らず、見えるのはトップページだけであるため、去る理由がないと思うからです。そのため、`top.location` が変わりません! + +動作: + +[codetabs src="top-location"] + +### Sandbox 属性 + +`sandbox` 属性によって制限されることの1つに、ナビゲーションがあります。サンドボックス化された iframe は、`top.location` を変更しない場合があります。 + +つまり、`sandbox="allow-scripts allow-forms"` を持つ iframe を追加します。これは制限を緩和し、スクリプト実行とフォーム送信を許可します。しかし、`top.location` の変更が禁止されるよう、`allow-top-navigation` は省略します。 + +これがそのコードです: + +```html +<iframe *!*sandbox="allow-scripts allow-forms"*/!* src="facebook.html"></iframe> +``` + +この単純な防御を回避する方法は他にもあります。 + +## X-Frame-Options + +サーバサイドのヘッダ `X-Frame-Options` は、フレーム内にページを表示することを許可または禁止することができます。 + +これは *サーバから* 送られなければなりません。`<meta>` タグの中でそれを見つけても、ブラウザは無視します。したがって、`<meta http-equiv="X-Frame-Options"...>` は何もしません。 + +このヘッダには3つの値があります: + + +`DENY` +: 決してフレーム内にページを表示しません。 + +`SAMEORIGIN` +: 親のドキュメントが同じオリジンから来ている場合、フレームの内側を許可します。 + +`ALLOW-FROM domain` +: 親のドキュメントが指定されたドメインからのものである場合、フレームの内側を許可します。 + +例えば、Twitter は `X-Frame-Options: SAMEORIGIN` を使っています。 + +````online +動作確認: + +```html +<iframe src="https://twitter.com"></iframe> +``` + +<iframe src="https://twitter.com"></iframe> + +利用しているブラウザによって、上の `iframe` は空、またはブラウザがそのページをこの方法では移動することを許可しないことを警告するものになります。 +```` + +## 機能性を無効にして表示する + +`X-Frame-Options` ヘッダには副作用があります。他のサイトでは、たとえ正当な理由があったとしても、ページをフレーム内に表示することはできません。 + +そのため、他の解決策もあります。例えば、すべてのクリックを遮断するよう、`height: 100%; width: 100%;` を持つ `<div>` でページを "覆う" ことができます。`window == top` または、保護の必要がないと判断した場合には、 `<div>` は消えるようにします。 + +このようなものです: + +```html +<style> + #protector { + height: 100%; + width: 100%; + position: absolute; + left: 0; + top: 0; + z-index: 99999999; + } +</style> + +<div id="protector"> + <a href="/" target="_blank">Go to the site</a> +</div> + +<script> + // トップのウィンドウが異なるオリジンからのものであればエラーになります + // ここでは OK です + if (top.document.domain == document.domain) { + protector.remove(); + } +</script> +``` + +デモ: + +[codetabs src="protector"] + +## サマリ + +クリックジャッキングは、ユーザが何が起きているか知らずに、"騙して" 悪意のあるサイト上でクリックをさせる方法です。重要なクリックによる操作がある場合、それは危険です。 + +ハッカーは自身の悪意のあるページへのリンクをメッセージで投稿したり、他の手段を使って訪問者を自分のページに誘導します。それには様々なバリエーションがあります。 + +ある観点から言えば、攻撃は "深く" はありません。ハッカーが行っているのはシングルクリックの傍受/横取りだけです。しかし、別の観点で言えば、ハッカーがクリックの後に別の制御/操作画面が表示されることを知っている場合、狡猾なメッセージを使用して、同様にユーザにそれらをクリックさせることもできます。 + +この攻撃は非常に危険です。なぜなら、我々がUIを設計するとき、通常はハッカーが訪問者の代わりにクリックすることは予期しないからです。そのため、まったく予想外の場所で脆弱性が見つかる可能性があります。 + +- フレームの内側に表示されることを意図していないページ(またはWebサイト全体)には`X-Frame-Options: SAMEORIGIN` を利用することを推奨します。 +- ページを iframe で表示することを許可したいが、安全を維持したい場合には、覆う `<div>` を使用してください。 diff --git a/4-frames-and-windows/06-clickjacking/clickjacking-visible.view/facebook.html b/3-frames-and-windows/06-clickjacking/clickjacking-visible.view/facebook.html similarity index 100% rename from 4-frames-and-windows/06-clickjacking/clickjacking-visible.view/facebook.html rename to 3-frames-and-windows/06-clickjacking/clickjacking-visible.view/facebook.html diff --git a/4-frames-and-windows/06-clickjacking/clickjacking-visible.view/index.html b/3-frames-and-windows/06-clickjacking/clickjacking-visible.view/index.html similarity index 100% rename from 4-frames-and-windows/06-clickjacking/clickjacking-visible.view/index.html rename to 3-frames-and-windows/06-clickjacking/clickjacking-visible.view/index.html diff --git a/4-frames-and-windows/06-clickjacking/clickjacking.view/facebook.html b/3-frames-and-windows/06-clickjacking/clickjacking.view/facebook.html similarity index 100% rename from 4-frames-and-windows/06-clickjacking/clickjacking.view/facebook.html rename to 3-frames-and-windows/06-clickjacking/clickjacking.view/facebook.html diff --git a/4-frames-and-windows/06-clickjacking/clickjacking.view/index.html b/3-frames-and-windows/06-clickjacking/clickjacking.view/index.html similarity index 100% rename from 4-frames-and-windows/06-clickjacking/clickjacking.view/index.html rename to 3-frames-and-windows/06-clickjacking/clickjacking.view/index.html diff --git a/4-frames-and-windows/06-clickjacking/protector.view/iframe.html b/3-frames-and-windows/06-clickjacking/protector.view/iframe.html similarity index 100% rename from 4-frames-and-windows/06-clickjacking/protector.view/iframe.html rename to 3-frames-and-windows/06-clickjacking/protector.view/iframe.html diff --git a/4-frames-and-windows/06-clickjacking/protector.view/index.html b/3-frames-and-windows/06-clickjacking/protector.view/index.html similarity index 100% rename from 4-frames-and-windows/06-clickjacking/protector.view/index.html rename to 3-frames-and-windows/06-clickjacking/protector.view/index.html diff --git a/4-frames-and-windows/06-clickjacking/top-location.view/iframe.html b/3-frames-and-windows/06-clickjacking/top-location.view/iframe.html similarity index 100% rename from 4-frames-and-windows/06-clickjacking/top-location.view/iframe.html rename to 3-frames-and-windows/06-clickjacking/top-location.view/iframe.html diff --git a/4-frames-and-windows/06-clickjacking/top-location.view/index.html b/3-frames-and-windows/06-clickjacking/top-location.view/index.html similarity index 100% rename from 4-frames-and-windows/06-clickjacking/top-location.view/index.html rename to 3-frames-and-windows/06-clickjacking/top-location.view/index.html diff --git a/4-frames-and-windows/index.md b/3-frames-and-windows/index.md similarity index 100% rename from 4-frames-and-windows/index.md rename to 3-frames-and-windows/index.md diff --git a/4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/solution.js b/4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/solution.js new file mode 100644 index 0000000000..2f51384efb --- /dev/null +++ b/4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/solution.js @@ -0,0 +1,18 @@ +function concat(arrays) { + // sum of individual array lengths + let totalLength = arrays.reduce((acc, value) => acc + value.length, 0); + + if (!arrays.length) return null; + + let result = new Uint8Array(totalLength); + + // for each array - copy it over result + // next array is copied right after the previous one + let length = 0; + for(let array of arrays) { + result.set(array, length); + length += array.length; + } + + return result; +} diff --git a/4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/source.js b/4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/source.js new file mode 100644 index 0000000000..e88b1a5379 --- /dev/null +++ b/4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/source.js @@ -0,0 +1,13 @@ +function concat(arrays) { + // ...your code... +} + +let chunks = [ + new Uint8Array([0, 1, 2]), + new Uint8Array([3, 4, 5]), + new Uint8Array([6, 7, 8]) +]; + +console.log(Array.from(concat(chunks))); // 0, 1, 2, 3, 4, 5, 6, 7, 8 + +console.log(concat(chunks).constructor.name); // Uint8Array diff --git a/4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/test.js b/4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/test.js new file mode 100644 index 0000000000..bcb19daef1 --- /dev/null +++ b/4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/test.js @@ -0,0 +1,31 @@ +describe("concat", function() { + let chunks = [ + new Uint8Array([0, 1, 2]), + new Uint8Array([3, 4, 5]), + new Uint8Array([6, 7, 8]) + ]; + + it("result has the same array type", function() { + + let result = concat(chunks); + + assert.equal(result.constructor, Uint8Array); + }); + + it("concatenates arrays", function() { + + let result = concat(chunks); + + assert.deepEqual(result, new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8])); + + }); + + it("returns empty array on empty input", function() { + + let result = concat([]); + + assert.equal(result.length, 0); + + }); + +}); diff --git a/6-async/04-promise-api/02-promise-errors-as-results-2/solution.md b/4-binary/01-arraybuffer-binary-arrays/01-concat/solution.md similarity index 100% rename from 6-async/04-promise-api/02-promise-errors-as-results-2/solution.md rename to 4-binary/01-arraybuffer-binary-arrays/01-concat/solution.md diff --git a/4-binary/01-arraybuffer-binary-arrays/01-concat/task.md b/4-binary/01-arraybuffer-binary-arrays/01-concat/task.md new file mode 100644 index 0000000000..fc131c5685 --- /dev/null +++ b/4-binary/01-arraybuffer-binary-arrays/01-concat/task.md @@ -0,0 +1,4 @@ + +# 型付き配列を連結する + +`Uint8Array` の配列を引数として、それらを連結した単一の配列を返す関数 `concat(arrays)` を書いてください。 diff --git a/4-binary/01-arraybuffer-binary-arrays/8bit-integer-256.svg b/4-binary/01-arraybuffer-binary-arrays/8bit-integer-256.svg new file mode 100644 index 0000000000..b697d63043 --- /dev/null +++ b/4-binary/01-arraybuffer-binary-arrays/8bit-integer-256.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="195" height="145" viewBox="0 0 195 145"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="binary" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="8bit-integer-256.svg"><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M24 33h100v28H24z"/><path id="100000000" fill="#181717" fill-rule="nonzero" d="M14.332 49.848h2.512v-8.416l-2.56 1.792-.624-.912 3.52-2.512h.896v10.048h2.464V51h-6.208v-1.152zM25.78 45.4c0-.907.08-1.72.24-2.44.16-.72.4-1.328.72-1.824.32-.496.725-.875 1.216-1.136.49-.261 1.072-.392 1.744-.392.715 0 1.32.128 1.816.384s.899.63 1.208 1.12c.31.49.533 1.096.672 1.816s.208 1.544.208 2.472c0 .907-.08 1.72-.24 2.44-.16.72-.4 1.328-.72 1.824-.32.496-.725.875-1.216 1.136-.49.261-1.072.392-1.744.392-.704 0-1.304-.141-1.8-.424a3.379 3.379 0 01-1.216-1.192c-.315-.512-.541-1.123-.68-1.832-.139-.71-.208-1.49-.208-2.344zm6.512 0c0-.565-.032-1.104-.096-1.616l-4.752 4.336c.181.608.453 1.093.816 1.456.363.363.837.544 1.424.544.939 0 1.608-.39 2.008-1.168.4-.779.6-1.963.6-3.552zm-5.184 0c0 .267.008.523.024.768s.035.485.056.72l4.768-4.32c-.181-.576-.45-1.035-.808-1.376-.357-.341-.84-.512-1.448-.512-.95 0-1.619.392-2.008 1.176-.39.784-.584 1.965-.584 3.544zm11.272 0c0-.907.08-1.72.24-2.44.16-.72.4-1.328.72-1.824.32-.496.725-.875 1.216-1.136.49-.261 1.072-.392 1.744-.392.715 0 1.32.128 1.816.384s.899.63 1.208 1.12c.31.49.533 1.096.672 1.816s.208 1.544.208 2.472c0 .907-.08 1.72-.24 2.44-.16.72-.4 1.328-.72 1.824-.32.496-.725.875-1.216 1.136-.49.261-1.072.392-1.744.392-.704 0-1.304-.141-1.8-.424a3.379 3.379 0 01-1.216-1.192c-.315-.512-.541-1.123-.68-1.832-.139-.71-.208-1.49-.208-2.344zm6.512 0c0-.565-.032-1.104-.096-1.616l-4.752 4.336c.181.608.453 1.093.816 1.456.363.363.837.544 1.424.544.939 0 1.608-.39 2.008-1.168.4-.779.6-1.963.6-3.552zm-5.184 0c0 .267.008.523.024.768s.035.485.056.72l4.768-4.32c-.181-.576-.45-1.035-.808-1.376-.357-.341-.84-.512-1.448-.512-.95 0-1.619.392-2.008 1.176-.39.784-.584 1.965-.584 3.544zm11.272 0c0-.907.08-1.72.24-2.44.16-.72.4-1.328.72-1.824.32-.496.725-.875 1.216-1.136.49-.261 1.072-.392 1.744-.392.715 0 1.32.128 1.816.384s.899.63 1.208 1.12c.31.49.533 1.096.672 1.816s.208 1.544.208 2.472c0 .907-.08 1.72-.24 2.44-.16.72-.4 1.328-.72 1.824-.32.496-.725.875-1.216 1.136-.49.261-1.072.392-1.744.392-.704 0-1.304-.141-1.8-.424a3.379 3.379 0 01-1.216-1.192c-.315-.512-.541-1.123-.68-1.832-.139-.71-.208-1.49-.208-2.344zm6.512 0c0-.565-.032-1.104-.096-1.616l-4.752 4.336c.181.608.453 1.093.816 1.456.363.363.837.544 1.424.544.939 0 1.608-.39 2.008-1.168.4-.779.6-1.963.6-3.552zm-5.184 0c0 .267.008.523.024.768s.035.485.056.72l4.768-4.32c-.181-.576-.45-1.035-.808-1.376-.357-.341-.84-.512-1.448-.512-.95 0-1.619.392-2.008 1.176-.39.784-.584 1.965-.584 3.544zm11.272 0c0-.907.08-1.72.24-2.44.16-.72.4-1.328.72-1.824.32-.496.725-.875 1.216-1.136.49-.261 1.072-.392 1.744-.392.715 0 1.32.128 1.816.384s.899.63 1.208 1.12c.31.49.533 1.096.672 1.816s.208 1.544.208 2.472c0 .907-.08 1.72-.24 2.44-.16.72-.4 1.328-.72 1.824-.32.496-.725.875-1.216 1.136-.49.261-1.072.392-1.744.392-.704 0-1.304-.141-1.8-.424a3.379 3.379 0 01-1.216-1.192c-.315-.512-.541-1.123-.68-1.832-.139-.71-.208-1.49-.208-2.344zm6.512 0c0-.565-.032-1.104-.096-1.616l-4.752 4.336c.181.608.453 1.093.816 1.456.363.363.837.544 1.424.544.939 0 1.608-.39 2.008-1.168.4-.779.6-1.963.6-3.552zm-5.184 0c0 .267.008.523.024.768s.035.485.056.72l4.768-4.32c-.181-.576-.45-1.035-.808-1.376-.357-.341-.84-.512-1.448-.512-.95 0-1.619.392-2.008 1.176-.39.784-.584 1.965-.584 3.544zm11.272 0c0-.907.08-1.72.24-2.44.16-.72.4-1.328.72-1.824.32-.496.725-.875 1.216-1.136.49-.261 1.072-.392 1.744-.392.715 0 1.32.128 1.816.384s.899.63 1.208 1.12c.31.49.533 1.096.672 1.816s.208 1.544.208 2.472c0 .907-.08 1.72-.24 2.44-.16.72-.4 1.328-.72 1.824-.32.496-.725.875-1.216 1.136-.49.261-1.072.392-1.744.392-.704 0-1.304-.141-1.8-.424a3.379 3.379 0 01-1.216-1.192c-.315-.512-.541-1.123-.68-1.832-.139-.71-.208-1.49-.208-2.344zm6.512 0c0-.565-.032-1.104-.096-1.616l-4.752 4.336c.181.608.453 1.093.816 1.456.363.363.837.544 1.424.544.939 0 1.608-.39 2.008-1.168.4-.779.6-1.963.6-3.552zm-5.184 0c0 .267.008.523.024.768s.035.485.056.72l4.768-4.32c-.181-.576-.45-1.035-.808-1.376-.357-.341-.84-.512-1.448-.512-.95 0-1.619.392-2.008 1.176-.39.784-.584 1.965-.584 3.544zm11.272 0c0-.907.08-1.72.24-2.44.16-.72.4-1.328.72-1.824.32-.496.725-.875 1.216-1.136.49-.261 1.072-.392 1.744-.392.715 0 1.32.128 1.816.384s.899.63 1.208 1.12c.31.49.533 1.096.672 1.816s.208 1.544.208 2.472c0 .907-.08 1.72-.24 2.44-.16.72-.4 1.328-.72 1.824-.32.496-.725.875-1.216 1.136-.49.261-1.072.392-1.744.392-.704 0-1.304-.141-1.8-.424a3.379 3.379 0 01-1.216-1.192c-.315-.512-.541-1.123-.68-1.832-.139-.71-.208-1.49-.208-2.344zm6.512 0c0-.565-.032-1.104-.096-1.616l-4.752 4.336c.181.608.453 1.093.816 1.456.363.363.837.544 1.424.544.939 0 1.608-.39 2.008-1.168.4-.779.6-1.963.6-3.552zm-5.184 0c0 .267.008.523.024.768s.035.485.056.72l4.768-4.32c-.181-.576-.45-1.035-.808-1.376-.357-.341-.84-.512-1.448-.512-.95 0-1.619.392-2.008 1.176-.39.784-.584 1.965-.584 3.544zm11.272 0c0-.907.08-1.72.24-2.44.16-.72.4-1.328.72-1.824.32-.496.725-.875 1.216-1.136.49-.261 1.072-.392 1.744-.392.715 0 1.32.128 1.816.384s.899.63 1.208 1.12c.31.49.533 1.096.672 1.816s.208 1.544.208 2.472c0 .907-.08 1.72-.24 2.44-.16.72-.4 1.328-.72 1.824-.32.496-.725.875-1.216 1.136-.49.261-1.072.392-1.744.392-.704 0-1.304-.141-1.8-.424a3.379 3.379 0 01-1.216-1.192c-.315-.512-.541-1.123-.68-1.832-.139-.71-.208-1.49-.208-2.344zm6.512 0c0-.565-.032-1.104-.096-1.616l-4.752 4.336c.181.608.453 1.093.816 1.456.363.363.837.544 1.424.544.939 0 1.608-.39 2.008-1.168.4-.779.6-1.963.6-3.552zm-5.184 0c0 .267.008.523.024.768s.035.485.056.72l4.768-4.32c-.181-.576-.45-1.035-.808-1.376-.357-.341-.84-.512-1.448-.512-.95 0-1.619.392-2.008 1.176-.39.784-.584 1.965-.584 3.544zm11.272 0c0-.907.08-1.72.24-2.44.16-.72.4-1.328.72-1.824.32-.496.725-.875 1.216-1.136.49-.261 1.072-.392 1.744-.392.715 0 1.32.128 1.816.384s.899.63 1.208 1.12c.31.49.533 1.096.672 1.816s.208 1.544.208 2.472c0 .907-.08 1.72-.24 2.44-.16.72-.4 1.328-.72 1.824-.32.496-.725.875-1.216 1.136-.49.261-1.072.392-1.744.392-.704 0-1.304-.141-1.8-.424a3.379 3.379 0 01-1.216-1.192c-.315-.512-.541-1.123-.68-1.832-.139-.71-.208-1.49-.208-2.344zm6.512 0c0-.565-.032-1.104-.096-1.616l-4.752 4.336c.181.608.453 1.093.816 1.456.363.363.837.544 1.424.544.939 0 1.608-.39 2.008-1.168.4-.779.6-1.963.6-3.552zm-5.184 0c0 .267.008.523.024.768s.035.485.056.72l4.768-4.32c-.181-.576-.45-1.035-.808-1.376-.357-.341-.84-.512-1.448-.512-.95 0-1.619.392-2.008 1.176-.39.784-.584 1.965-.584 3.544z"/><text id="8-bit-integer" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x="28.141" y="25">8-bit integer</tspan></text><text id="256" fill="#7E7C7B" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x="135.777" y="51">256</tspan></text></g></g></svg> \ No newline at end of file diff --git a/4-binary/01-arraybuffer-binary-arrays/8bit-integer-257.svg b/4-binary/01-arraybuffer-binary-arrays/8bit-integer-257.svg new file mode 100644 index 0000000000..8e3074fdf3 --- /dev/null +++ b/4-binary/01-arraybuffer-binary-arrays/8bit-integer-257.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="195" height="145" viewBox="0 0 195 145"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="binary" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="8bit-integer-257.svg"><path id="Rectangle-227-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M24 33h100v28H24z"/><path id="100000001" fill="#181717" fill-rule="nonzero" d="M14.332 49.848h2.512v-8.416l-2.56 1.792-.624-.912 3.52-2.512h.896v10.048h2.464V51h-6.208v-1.152zM25.78 45.4c0-.907.08-1.72.24-2.44.16-.72.4-1.328.72-1.824.32-.496.725-.875 1.216-1.136.49-.261 1.072-.392 1.744-.392.715 0 1.32.128 1.816.384s.899.63 1.208 1.12c.31.49.533 1.096.672 1.816s.208 1.544.208 2.472c0 .907-.08 1.72-.24 2.44-.16.72-.4 1.328-.72 1.824-.32.496-.725.875-1.216 1.136-.49.261-1.072.392-1.744.392-.704 0-1.304-.141-1.8-.424a3.379 3.379 0 01-1.216-1.192c-.315-.512-.541-1.123-.68-1.832-.139-.71-.208-1.49-.208-2.344zm6.512 0c0-.565-.032-1.104-.096-1.616l-4.752 4.336c.181.608.453 1.093.816 1.456.363.363.837.544 1.424.544.939 0 1.608-.39 2.008-1.168.4-.779.6-1.963.6-3.552zm-5.184 0c0 .267.008.523.024.768s.035.485.056.72l4.768-4.32c-.181-.576-.45-1.035-.808-1.376-.357-.341-.84-.512-1.448-.512-.95 0-1.619.392-2.008 1.176-.39.784-.584 1.965-.584 3.544zm11.272 0c0-.907.08-1.72.24-2.44.16-.72.4-1.328.72-1.824.32-.496.725-.875 1.216-1.136.49-.261 1.072-.392 1.744-.392.715 0 1.32.128 1.816.384s.899.63 1.208 1.12c.31.49.533 1.096.672 1.816s.208 1.544.208 2.472c0 .907-.08 1.72-.24 2.44-.16.72-.4 1.328-.72 1.824-.32.496-.725.875-1.216 1.136-.49.261-1.072.392-1.744.392-.704 0-1.304-.141-1.8-.424a3.379 3.379 0 01-1.216-1.192c-.315-.512-.541-1.123-.68-1.832-.139-.71-.208-1.49-.208-2.344zm6.512 0c0-.565-.032-1.104-.096-1.616l-4.752 4.336c.181.608.453 1.093.816 1.456.363.363.837.544 1.424.544.939 0 1.608-.39 2.008-1.168.4-.779.6-1.963.6-3.552zm-5.184 0c0 .267.008.523.024.768s.035.485.056.72l4.768-4.32c-.181-.576-.45-1.035-.808-1.376-.357-.341-.84-.512-1.448-.512-.95 0-1.619.392-2.008 1.176-.39.784-.584 1.965-.584 3.544zm11.272 0c0-.907.08-1.72.24-2.44.16-.72.4-1.328.72-1.824.32-.496.725-.875 1.216-1.136.49-.261 1.072-.392 1.744-.392.715 0 1.32.128 1.816.384s.899.63 1.208 1.12c.31.49.533 1.096.672 1.816s.208 1.544.208 2.472c0 .907-.08 1.72-.24 2.44-.16.72-.4 1.328-.72 1.824-.32.496-.725.875-1.216 1.136-.49.261-1.072.392-1.744.392-.704 0-1.304-.141-1.8-.424a3.379 3.379 0 01-1.216-1.192c-.315-.512-.541-1.123-.68-1.832-.139-.71-.208-1.49-.208-2.344zm6.512 0c0-.565-.032-1.104-.096-1.616l-4.752 4.336c.181.608.453 1.093.816 1.456.363.363.837.544 1.424.544.939 0 1.608-.39 2.008-1.168.4-.779.6-1.963.6-3.552zm-5.184 0c0 .267.008.523.024.768s.035.485.056.72l4.768-4.32c-.181-.576-.45-1.035-.808-1.376-.357-.341-.84-.512-1.448-.512-.95 0-1.619.392-2.008 1.176-.39.784-.584 1.965-.584 3.544zm11.272 0c0-.907.08-1.72.24-2.44.16-.72.4-1.328.72-1.824.32-.496.725-.875 1.216-1.136.49-.261 1.072-.392 1.744-.392.715 0 1.32.128 1.816.384s.899.63 1.208 1.12c.31.49.533 1.096.672 1.816s.208 1.544.208 2.472c0 .907-.08 1.72-.24 2.44-.16.72-.4 1.328-.72 1.824-.32.496-.725.875-1.216 1.136-.49.261-1.072.392-1.744.392-.704 0-1.304-.141-1.8-.424a3.379 3.379 0 01-1.216-1.192c-.315-.512-.541-1.123-.68-1.832-.139-.71-.208-1.49-.208-2.344zm6.512 0c0-.565-.032-1.104-.096-1.616l-4.752 4.336c.181.608.453 1.093.816 1.456.363.363.837.544 1.424.544.939 0 1.608-.39 2.008-1.168.4-.779.6-1.963.6-3.552zm-5.184 0c0 .267.008.523.024.768s.035.485.056.72l4.768-4.32c-.181-.576-.45-1.035-.808-1.376-.357-.341-.84-.512-1.448-.512-.95 0-1.619.392-2.008 1.176-.39.784-.584 1.965-.584 3.544zm11.272 0c0-.907.08-1.72.24-2.44.16-.72.4-1.328.72-1.824.32-.496.725-.875 1.216-1.136.49-.261 1.072-.392 1.744-.392.715 0 1.32.128 1.816.384s.899.63 1.208 1.12c.31.49.533 1.096.672 1.816s.208 1.544.208 2.472c0 .907-.08 1.72-.24 2.44-.16.72-.4 1.328-.72 1.824-.32.496-.725.875-1.216 1.136-.49.261-1.072.392-1.744.392-.704 0-1.304-.141-1.8-.424a3.379 3.379 0 01-1.216-1.192c-.315-.512-.541-1.123-.68-1.832-.139-.71-.208-1.49-.208-2.344zm6.512 0c0-.565-.032-1.104-.096-1.616l-4.752 4.336c.181.608.453 1.093.816 1.456.363.363.837.544 1.424.544.939 0 1.608-.39 2.008-1.168.4-.779.6-1.963.6-3.552zm-5.184 0c0 .267.008.523.024.768s.035.485.056.72l4.768-4.32c-.181-.576-.45-1.035-.808-1.376-.357-.341-.84-.512-1.448-.512-.95 0-1.619.392-2.008 1.176-.39.784-.584 1.965-.584 3.544zm11.272 0c0-.907.08-1.72.24-2.44.16-.72.4-1.328.72-1.824.32-.496.725-.875 1.216-1.136.49-.261 1.072-.392 1.744-.392.715 0 1.32.128 1.816.384s.899.63 1.208 1.12c.31.49.533 1.096.672 1.816s.208 1.544.208 2.472c0 .907-.08 1.72-.24 2.44-.16.72-.4 1.328-.72 1.824-.32.496-.725.875-1.216 1.136-.49.261-1.072.392-1.744.392-.704 0-1.304-.141-1.8-.424a3.379 3.379 0 01-1.216-1.192c-.315-.512-.541-1.123-.68-1.832-.139-.71-.208-1.49-.208-2.344zm6.512 0c0-.565-.032-1.104-.096-1.616l-4.752 4.336c.181.608.453 1.093.816 1.456.363.363.837.544 1.424.544.939 0 1.608-.39 2.008-1.168.4-.779.6-1.963.6-3.552zm-5.184 0c0 .267.008.523.024.768s.035.485.056.72l4.768-4.32c-.181-.576-.45-1.035-.808-1.376-.357-.341-.84-.512-1.448-.512-.95 0-1.619.392-2.008 1.176-.39.784-.584 1.965-.584 3.544zm11.272 0c0-.907.08-1.72.24-2.44.16-.72.4-1.328.72-1.824.32-.496.725-.875 1.216-1.136.49-.261 1.072-.392 1.744-.392.715 0 1.32.128 1.816.384s.899.63 1.208 1.12c.31.49.533 1.096.672 1.816s.208 1.544.208 2.472c0 .907-.08 1.72-.24 2.44-.16.72-.4 1.328-.72 1.824-.32.496-.725.875-1.216 1.136-.49.261-1.072.392-1.744.392-.704 0-1.304-.141-1.8-.424a3.379 3.379 0 01-1.216-1.192c-.315-.512-.541-1.123-.68-1.832-.139-.71-.208-1.49-.208-2.344zm6.512 0c0-.565-.032-1.104-.096-1.616l-4.752 4.336c.181.608.453 1.093.816 1.456.363.363.837.544 1.424.544.939 0 1.608-.39 2.008-1.168.4-.779.6-1.963.6-3.552zm-5.184 0c0 .267.008.523.024.768s.035.485.056.72l4.768-4.32c-.181-.576-.45-1.035-.808-1.376-.357-.341-.84-.512-1.448-.512-.95 0-1.619.392-2.008 1.176-.39.784-.584 1.965-.584 3.544zm12.424 4.448h2.512v-8.416l-2.56 1.792-.624-.912 3.52-2.512h.896v10.048h2.464V51h-6.208v-1.152z"/><text id="8-bit-integer" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x="30.141" y="25">8-bit integer</tspan></text><text id="257" fill="#7E7C7B" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x="133.777" y="50">257</tspan></text></g></g></svg> \ No newline at end of file diff --git a/4-binary/01-arraybuffer-binary-arrays/arraybuffer-view-buffersource.svg b/4-binary/01-arraybuffer-binary-arrays/arraybuffer-view-buffersource.svg new file mode 100644 index 0000000000..b9de47de1e --- /dev/null +++ b/4-binary/01-arraybuffer-binary-arrays/arraybuffer-view-buffersource.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="707" height="463" viewBox="0 0 707 463"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="binary" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="arraybuffer-view-buffersource.svg"><path id="Rectangle" fill="#D1CFCD" stroke="#7E7C7B" d="M238.5 44.5h449v391h-449z"/><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M237 61h31v58h-31z"/><path id="Rectangle-227-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M268 61h28v58h-28z"/><path id="Rectangle-227-Copy-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M296 61h28v58h-28z"/><path id="Rectangle-227-Copy-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M324 61h28v58h-28z"/><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M352 61h28v58h-28z"/><path id="Rectangle-227-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M380 61h28v58h-28z"/><path id="Rectangle-227-Copy-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M408 61h28v58h-28z"/><path id="Rectangle-227-Copy-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M436 61h28v58h-28z"/><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M463 61h28v58h-28z"/><path id="Rectangle-227-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M491 61h28v58h-28z"/><path id="Rectangle-227-Copy-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M519 61h28v58h-28z"/><path id="Rectangle-227-Copy-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M547 61h28v58h-28z"/><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M575 61h28v58h-28z"/><path id="Rectangle-227-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M603 61h28v58h-28z"/><path id="Rectangle-227-Copy-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M631 61h28v58h-28z"/><path id="Rectangle-227-Copy-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M659 61h28v58h-28z"/><path id="Rectangle-227-Copy-19" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M237 155h59v44h-59z"/><path id="Rectangle-227-Copy-22" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M82 155h157v44H82z"/><path id="Rectangle-227-Copy-17" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M296 155h56v44h-56z"/><path id="Rectangle-227-Copy-15" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M352 155h56v44h-56z"/><text id="0-copy" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="263.8" y="180">0</tspan></text><text id="2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="375.8" y="180">2</tspan></text><text id="1" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="320.8" y="180">1</tspan></text><path id="Rectangle-227-Copy-13" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M408 155h56v44h-56z"/><text id="3" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="431.8" y="180">3</tspan></text><path id="Rectangle-227-Copy-11" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M463 155h56v44h-56z"/><path id="Rectangle-227-Copy-9" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M519 155h56v44h-56z"/><path id="Rectangle-227-Copy-7" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M575 155h56v44h-56z"/><path id="Rectangle-227-Copy-5" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M631 155h56v44h-56z"/><text id="4" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="485.8" y="180">4</tspan></text><text id="5" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="541.8" y="180">5</tspan></text><text id="6" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="597.8" y="180">6</tspan></text><text id="7" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="654.8" y="180">7</tspan></text><path id="Rectangle-227-Copy-20" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M236 234h116v58H236z"/><path id="Rectangle-227-Copy-23" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M238 234v58H82v-58h156z"/><path id="Rectangle-227-Copy-16" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M352 234h112v58H352z"/><text id="0-copy-2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="293" y="267">0</tspan></text><text id="1" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="404" y="267">1</tspan></text><path id="Rectangle-227-Copy-12" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M463 234h112v58H463z"/><path id="Rectangle-227-Copy-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M575 234h112v58H575z"/><text id="2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="516" y="267">2</tspan></text><text id="3" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="628" y="267">3</tspan></text><path id="Rectangle-227-Copy-21" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M237 328h227v28H237z"/><text id="0" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="348.8" y="346">0</tspan></text><path id="Rectangle-227-Copy-14" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M463 328h224v28H463z"/><text id="1" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="569.8" y="345">1</tspan></text><text id="new-ArrayBuffer(16)" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="330.8" y="26">new ArrayBuffer(16)</tspan></text><text id="ArrayBufferView" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal" transform="rotate(-90 50 236)"><tspan x="-22" y="241">ArrayBufferView</tspan></text><text id="Uint16Array-Int16Arr" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="115.3" y="172">Uint16Array</tspan> <tspan x="119.5" y="188">Int16Array</tspan></text><path id="Rectangle-227-Copy-25" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M82 61h157v58H82z"/><text id="Uint8Array-Int8Array" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="119.5" y="79">Uint8Array</tspan> <tspan x="123.7" y="94">Int8Array</tspan> <tspan x="90.1" y="109">Uint8ClampedArray</tspan></text><text id="Uint32Array-Int32Arr" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="114.3" y="248">Uint32Array</tspan> <tspan x="118.5" y="264">Int32Array</tspan> <tspan x="110.1" y="280">Float32Array</tspan></text><path id="Rectangle-227-Copy-24" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M238 328v28H82v-28h156z"/><path id="Rectangle-227-Copy-27" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M238 391h449v28H238z"/><path id="Rectangle-227-Copy-26" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M238 391v28H82v-28h156z"/><text id="Float64Array-Copy" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="111.1" y="345">Float64Array</tspan></text><text id="DataView" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="128.4" y="408">DataView</tspan></text><text id="get/setUint8(offset)" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="243.3" y="409">get/setUint8(offset) get/setFloat32(offset)...</tspan></text><text id="BufferSource" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="11.4" y="28">BufferSource</tspan></text><path id="Line-2" fill="#C06334" fill-rule="nonzero" d="M51.5 42.5v51h8l-9.5 19-9.5-19h8v-51h3z"/><path id="Line-2-Copy" fill="#C06334" fill-rule="nonzero" d="M192.5 14.5l19 9.5-19 9.5v-8h-49v-3h49v-8z"/><text id="1" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="276.8" y="92">1</tspan></text><text id="0" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="249.8" y="92">0</tspan></text><text id="2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="304.8" y="92">2</tspan></text><text id="3" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="332.8" y="92">3</tspan></text><text id="4" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="360.8" y="92">4</tspan></text><text id="5" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="388.8" y="92">5</tspan></text><text id="6" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="416.8" y="92">6</tspan></text><text id="7" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="444.8" y="92">7</tspan></text><text id="8" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="471.8" y="92">8</tspan></text><text id="9" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="499.8" y="92">9</tspan></text><text id="10" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="524.6" y="92">10</tspan></text><text id="11" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="551.6" y="92">11</tspan></text><text id="12" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="579.6" y="92">12</tspan></text><text id="13" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="608.6" y="92">13</tspan></text><text id="14" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="636.6" y="92">14</tspan></text><text id="15" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="663.6" y="92">15</tspan></text></g></g></svg> \ No newline at end of file diff --git a/4-binary/01-arraybuffer-binary-arrays/arraybuffer-views.svg b/4-binary/01-arraybuffer-binary-arrays/arraybuffer-views.svg new file mode 100644 index 0000000000..b022796ad2 --- /dev/null +++ b/4-binary/01-arraybuffer-binary-arrays/arraybuffer-views.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="603" height="286" viewBox="0 0 603 286"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="binary" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="arraybuffer-views.svg"><path id="Rectangle" fill="#D1CFCD" stroke="#7E7C7B" d="M127.5 52.5h448v198h-448z"/><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M126 72h31v28h-31z"/><path id="Rectangle-227-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M157 72h28v28h-28z"/><path id="Rectangle-227-Copy-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M185 72h28v28h-28z"/><path id="Rectangle-227-Copy-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M213 72h28v28h-28z"/><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M241 72h28v28h-28z"/><path id="Rectangle-227-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M269 72h28v28h-28z"/><path id="Rectangle-227-Copy-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M297 72h28v28h-28z"/><path id="Rectangle-227-Copy-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M325 72h28v28h-28z"/><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M352 72h28v28h-28z"/><path id="Rectangle-227-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M380 72h28v28h-28z"/><path id="Rectangle-227-Copy-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M408 72h28v28h-28z"/><path id="Rectangle-227-Copy-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M436 72h28v28h-28z"/><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M464 72h28v28h-28z"/><path id="Rectangle-227-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M492 72h28v28h-28z"/><path id="Rectangle-227-Copy-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M520 72h28v28h-28z"/><path id="Rectangle-227-Copy-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M548 72h28v28h-28z"/><path id="Rectangle-227-Copy-19" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M126 116h59v28h-59z"/><path id="Rectangle-227-Copy-22" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M21 116h107v28H21z"/><path id="Rectangle-227-Copy-17" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M185 116h56v28h-56z"/><path id="Rectangle-227-Copy-15" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M241 116h56v28h-56z"/><text id="1" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="166.8" y="89">1</tspan></text><text id="0" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="139.8" y="89">0</tspan></text><text id="0-copy" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="152.8" y="134">0</tspan></text><text id="2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="264.8" y="134">2</tspan></text><text id="1" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="209.8" y="134">1</tspan></text><path id="Rectangle-227-Copy-13" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M297 116h56v28h-56z"/><text id="3" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="320.8" y="134">3</tspan></text><path id="Rectangle-227-Copy-11" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M352 116h56v28h-56z"/><path id="Rectangle-227-Copy-9" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M408 116h56v28h-56z"/><path id="Rectangle-227-Copy-7" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M464 116h56v28h-56z"/><path id="Rectangle-227-Copy-5" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M520 116h56v28h-56z"/><text id="2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="194.8" y="89">2</tspan></text><text id="3" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="222.8" y="89">3</tspan></text><text id="4" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="250.8" y="89">4</tspan></text><text id="5" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="278.8" y="89">5</tspan></text><text id="6" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="306.8" y="89">6</tspan></text><text id="7" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="334.8" y="89">7</tspan></text><text id="8" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="361.8" y="89">8</tspan></text><text id="9" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="389.8" y="89">9</tspan></text><text id="10" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="413.6" y="89">10</tspan></text><text id="11" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="440.6" y="89">11</tspan></text><text id="12" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="469.6" y="89">12</tspan></text><text id="13" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="497.6" y="89">13</tspan></text><text id="14" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="525.6" y="89">14</tspan></text><text id="15" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="553.6" y="89">15</tspan></text><text id="4" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="374.8" y="134">4</tspan></text><text id="5" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="430.8" y="134">5</tspan></text><text id="6" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="486.8" y="134">6</tspan></text><text id="7" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="543.8" y="134">7</tspan></text><path id="Rectangle-227-Copy-20" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M125 160h116v28H125z"/><path id="Rectangle-227-Copy-23" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M21 160h106v28H21z"/><path id="Rectangle-227-Copy-16" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M241 160h112v28H241z"/><text id="0-copy-2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="180.8" y="178">0</tspan></text><text id="1" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="291.8" y="178">1</tspan></text><path id="Rectangle-227-Copy-12" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M352 160h112v28H352z"/><path id="Rectangle-227-Copy-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M464 160h112v28H464z"/><text id="2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="403.8" y="178">2</tspan></text><text id="3" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="516.8" y="178">3</tspan></text><path id="Rectangle-227-Copy-21" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M126 204h227v28H126z"/><text id="0" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="237.8" y="222">0</tspan></text><path id="Rectangle-227-Copy-14" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M352 204h224v28H352z"/><text id="1" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="458.8" y="221">1</tspan></text><text id="new-ArrayBuffer(16)" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="121.7" y="38">new ArrayBuffer(16)</tspan></text><text id="Uint16Array" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="28.3" y="133">Uint16Array</tspan></text><path id="Rectangle-227-Copy-25" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M21 72h107v28H21z"/><text id="Uint8Array" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="32.5" y="89">Uint8Array</tspan></text><text id="Uint32Array" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="27.3" y="177">Uint32Array</tspan></text><path id="Rectangle-227-Copy-24" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M21 204h106v28H21z"/><text id="Float64Array" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="23.1" y="221">Float64Array</tspan></text></g></g></svg> \ No newline at end of file diff --git a/4-binary/01-arraybuffer-binary-arrays/article.md b/4-binary/01-arraybuffer-binary-arrays/article.md new file mode 100644 index 0000000000..93756de82a --- /dev/null +++ b/4-binary/01-arraybuffer-binary-arrays/article.md @@ -0,0 +1,262 @@ +# ArrayBuffer, binary arrays + +Web 開発では、ファイル(作成、更新、ダウンロード)を処理するときにバイナリデータに出くわします。その他の典型的なユースケースは画像処理です。 + +これらはすべて JavaScript で可能です。また、バイナリ操作も高性能です。 + +ですが、多くのクラスが存在するため少し混乱しやすいです。いくつか例を挙げます: +- `ArrayBuffer`, `Uint8Array`, `DataView`, `Blob`, `File`, etc. + +JavaScript でのバイナリデータは、他の言語と比べて非標準的な方法で実装されています。しかし、一度整理できれば、すべてがとても簡単になります。 + +**基本となるバイナリオブジェクトは `ArrayBuffer` です。これは固定長の連続したメモリ領域への参照です。** + +次のようにして生成できます: +```js run +let buffer = new ArrayBuffer(16); // 長さ 16 のバッファを作成 +alert(buffer.byteLength); // 16 +``` + +16バイトの連続したメモリ領域が割り当てられ、事前にゼロで埋められます。 + +```warn header="`ArrayBuffer` は何かの配列ではありません" +混乱の元は排除しましょう。`ArrayBuffer` は `Array` とは何の共通点もありません。: +- 長さは固定で、増減することはできません。 +- メモリの中で、長さちょうどのスペースをとります。 +- 個々のバイトにアクセスするには、`buffer[index]` ではなく、別の "view" オブジェクトが必要です。 +``` + +`ArrayBuffer` はメモリ領域です。何が格納されているでしょう?それは分かりません。単に生のバイト列です。 + +**`ArrayBuffer` を操作するには、"ビュー(view)" オブジェクトを使用する必要があります。** + +ビューオブジェクトは自身には何も格納しません。それは `ArrayBuffer` に格納されたバイトへの解釈を与える "メガネ" です。 + +例: + +- **`Uint8Array`** -- `ArrayBuffer` にある各バイトを、 0 から 255 (1バイトは8ビット)までの値となる、別々の数として扱います。このような値は "8ビット符号なし整数(8-bit unsigned integer)" と呼ばれます。 +- **`Uint16Array`** -- 各2バイトを、0 から 65535 までの値となる整数として扱います。これは "16ビット符号なし整数" と呼ばれます。 +- **`Uint32Array`** -- 各4バイトを 0 から 4294967295 までの値となる整数として扱います。これは "32ビット符号なし整数" と呼ばれます。 +- **`Float64Array`** -- 各8バイトを <code>5.0x10<sup>-324</sup></code> から <code>1.8x10<sup>308</sup></code> までの値となる浮動小数点として扱います。 + +したがって、16バイトの `ArrayBuffer` にあるバイナリデータは 16 個の "小さい数字", あるいは 8 個のより大きい数字(2バイトずつ), または 4 個のより大きな数字(4バイトずつ), 高精度の2個の浮動小数点値(8バイトずつとして解釈できます。 + +![](arraybuffer-views.svg) + +`ArrayBuffer` はコアとなるオブジェクト、すべてのもののルート、生のバイナリデータです。 + +ですが、そこに書き込んだり反復しようとする場合、基本的にほぼすべての操作に対して、ビューを使用する必要があります。e.g: + +```js run +let buffer = new ArrayBuffer(16); // 長さ 16 のバッファを作成 + +*!* +let view = new Uint32Array(buffer); // 32 ビット整数列としてバッファを扱います + +alert(Uint32Array.BYTES_PER_ELEMENT); // 4 バイト毎の整数 +*/!* + +alert(view.length); // 4, 多くの整数が格納されます +alert(view.byteLength); // 16, バイトサイズ + +// 値の書き込み +view[0] = 123456; + +// 値のイテレート +for(let num of view) { + alert(num); // 123456, 次に 0, 0, 0 (全部で 4 つの値) +} + +``` + +## TypedArray + +これらすべてのビュー (`Uint8Array`, `Uint32Array`, etc) の共通の用語は、[TypedArray](https://tc39.github.io/ecma262/#sec-typedarray-objects) です。これらは同じメソッドとプロパティのセットを共有します。 + +これらは通常の配列によく似ています: インデックスがあり、反復可能(iterable)です。 + +型付き配列のコンストラクタ (`Int8Array` でも `Float64Array` でも構いません)は、引数の種類に応じて異なる振る舞いをします。 + +5 つの引数のパターンがあります: + +```js +new TypedArray(buffer, [byteOffset], [length]); +new TypedArray(object); +new TypedArray(typedArray); +new TypedArray(length); +new TypedArray(); +``` + +1. `ArrayBuffer` の引数が与えられると、それに対するビューが作られます。我々はすでにその構文を使いました。 + + オプションで、`byteOffset` で開始位置(デフォルトは 0)、`length` で長さ(デフォルトではバッファの終わりまで) を指定することができ、その場合はビューは `buffer` の一部だけをカバーします。 + +2. `Array` または配列ライクなオブジェクトが与えられた場合は、同じ長さの型付き配列を生成し、内容をコピーします。 + + これを使って配列にデータを事前に埋め込むことができます: + ```js run + *!* + let arr = new Uint8Array([0, 1, 2, 3]); + */!* + alert( arr.length ); // 4 + alert( arr[1] ); // 1 + ``` +3. 別の `TypedArray` が与えられた場合も同じです: 同じ長さの型付き配列を生成し、値をコピーします。値はその処理の中で新しい型に変換されます。 + ```js run + let arr16 = new Uint16Array([1, 1000]); + *!* + let arr8 = new Uint8Array(arr16); + */!* + alert( arr8[0] ); // 1 + alert( arr8[1] ); // 232 (1000 をコピーしようとしますが、1000 は 8 ビットには収まりません(1000 は 11 1110 1000, 232 は 1110 1000) + ``` + +4. 数値引数の場合は `length` であり、その数の要素を含む型付き配列を作成します。そのバイト長は、一つあたりのアイテムのバイト数 `TypedArray.BYTES_PER_ELEMENT` が `length` 倍されたものになります。: + ```js run + let arr = new Uint16Array(4); // 長さ 4 の型付き配列を作成します。 + alert( Uint16Array.BYTES_PER_ELEMENT ); // 要素あたり 2 バイトです + alert( arr.byteLength ); // 8 (バイトサイズ) + ``` + +5. 引数がなければ、長さゼロの型付き配列を作成します。 + +`ArrayBuffer` に言及することなく、直接 `TypedArray` を作成することができます。しかし、ビューはその根底にある `ArrayBuffer` なしでは存在できないため、最初のケース(`ArrayBuffer` が与えられた場合)を除く、すべての場合に自動的に作成されます。 + +`ArrayBuffer` にアクセスするには、次のプロパティを使います: +- `arr.buffer` -- `ArrayBuffer` への参照です。 +- `arr.byteLength` -- `ArrayBuffer` の長さです。 + +そのため、いつでもあるビューから別のビューへ移動させることができます。: +```js +let arr8 = new Uint8Array([0, 1, 2, 3]); + +// 同じデータを別のビューで +let arr16 = new Uint16Array(arr8.buffer); +``` + +型付き配列の一覧です: + +- `Uint8Array`, `Uint16Array`, `Uint32Array` -- 8, 16, 32 ビット整数の場合 + - `Uint8ClampedArray` -- 8ビット整数の場合、代入時に "クランプ" します (下記参照)。 +- `Int8Array`, `Int16Array`, `Int32Array` -- 符号付き整数の場合(負の値になることもあります)。 +- `Float32Array`, `Float64Array` -- 32, 64 ビット符号付き浮動小数点の場合。 + +```warn header="`int8` またはそれに類似した単一値の型はありません" +注意してください、`Int8Array` のような名前にもかかわらず、JavaScript には `int` あるいは `int8` のような単一値の型はありません。 + +繰り返しますが、`Int8Array` はこれらの個々の値の配列ではなく、`ArrayBuffer` のビューです。 +``` + +### 範囲外の動作 + +もし範囲外の値を型付き配列に書き込もうとするとどうなるでしょう?エラーにはなりませんが、余分なビットが切り捨てられます。 + +例えば、256 を `Uint8Array` に置くことを考えてみましょう。二進法(バイナリ形式)では、256 は `100000000` (9ビット)ですが、`Uint8Array` は値ごとに 8 ビットしか提供せず、0 から 255 までの範囲です。 + +範囲を超えるようなより大きい数値の場合、(重要度の低い)右端の8ビットだけが格納され、残りは切り捨てられます。: + +![](8bit-integer-256.svg) + +そのため、ゼロになります。 + +257 の場合、二進法は `100000001` (9ビット)で、右端の 8ビットが格納されるので、配列には `1` が入ります。: + +![](8bit-integer-257.svg) + +デモです: + +```js run +let uint8array = new Uint8Array(16); + +let num = 256; +alert(num.toString(2)); // 100000000 (二進表現) + +uint8array[0] = 256; +uint8array[1] = 257; + +alert(uint8array[0]); // 0 +alert(uint8array[1]); // 1 +``` + +`Uint8ClampedArray` はこの点で特別で異なる振る舞いをします。これは、255 よりも大きな値の場合は 255 を、負の値の場合には 0 を格納します。これは画像処理の場合に役立つ振る舞いです。 + +## TypedArray メソッド + +`TypedArray` は通常の `Array` のメソッドを持っていますが、注目すべき例外があります。 + +`map`, `slice`, `find`, `reduce` 等を使用してイテレートすることができます。 + +ですが、できないことがいくつかあります: + +- `splice` はありません。型付き配列は buffer のビューであり、これらは固定された連続したメモリ領域であるため、値を "削除" することはできません。できることは、ゼロの代入です。 +- `concat` メソッドはありません。 + +2つの追加のメソッドがあります: + +- `arr.set(fromArr, [offset])` は `fromArr` のすべての要素を `arr` へ、位置 `offset` (デフォルトは 0) を開始点としてコピーします。 +- `arr.subarray([begin, end])` は `begin` から `end` (排他的)までの同じ型のビューを作成します。これは `slice` メソッドに似ています(このメソッドもサポートされています)が、何もコピーしません。単に指定されたデータ範囲を操作するための新しいビューを作成するだけです。 + +これらのメソッドにより、型付き配列のコピーしたり、ミックスしたり、既存の配列から新しい配列を作成したりすることができます。 + +## DataView + +[DataView](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView) は非常に柔軟な `ArrayBuffer` に対する "型指定のない" ビューです。あらゆるフォーマットの任意のオフセットのデータにアクセスすることができます。 + +- 型付き配列の場合、コンストラクタはフォーマットが何であるかを決定します。配列全体は不変になります。i 番目の数値は `arr[i]` です。 +- `DataView` では、`.getUint8(i)` や `.getUint16(i)` メソッドでデータにアクセスします。フォーマットはコンストラクタ時の代わりに、メソッド呼び出し時に指定します。 + +構文: + +```js +new DataView(buffer, [byteOffset], [byteLength]) +``` + +- **`buffer`** -- 基礎となる `ArrayBuffer` です。型付き配列とは異なり、`DataView` はそれ自身では buffer は作成しません。事前に用意する必要があります。 +- **`byteOffset`** -- ビューの開始バイト位置(デフォルトは 0)です。 +- **`byteLength`** -- ビューのバイト長(デフォルトは `buffer` の最後まで)です。 + +例えば、ここでは同じバッファから異なるフォーマットで数値を取り出します。: + +```js run +let buffer = new Uint8Array([255, 255, 255, 255]).buffer; + +let dataView = new DataView(buffer); + +// オフセット 0 で、8ビットの数値を取得します +alert( dataView.getUint8(0) ); // 255 + +// オフセット 0 で、16ビットの数値を取得します、つまり 2バイトです +alert( dataView.getUint16(0) ); // 65535 (16ビット符号なし整数の最大値) + +// オフセット 0 で、32ビットの数値を取得します +alert( dataView.getUint32(0) ); // 4294967295 (32ビット符号なし整数の最大値) + +dataView.setUint32(0, 0); // 4バイトの数値を 0 にセット +``` + +`DataView` はフォーマットが混在したデータを同じバッファに格納するときに役立ちます。E.g 一連のペア(16ビット整数、32ビット float)を格納します。その後、`DataView` は簡単にそれらにアクセスすることができます。 + +## サマリ + +`ArrayBuffer` は基本となるオブジェクトで、固定長の連続したメモリ領域への参照です。 + +`ArrayBuffer` に対するほとんどの操作は、次のビューが必要です。 + +- `TypedArray`: + - `Uint8Array`, `Uint16Array`, `Uint32Array` -- 8, 16 あるいは 32 ビットの符号なし整数の場合 + - `Uint8ClampedArray` -- 8 ビット整数の場合。代入時に "クランプ" します + - `Int8Array`, `Int16Array`, `Int32Array` -- 符号あり整数値の場合(負の値もあり) + - `Float32Array`, `Float64Array` -- 32, 64 ビット符号あり浮動小数点 +- もしくは `DataView` -- フォーマットを指定するメソッドを使用するビューです。例えば、`getUint8(offset)`. + +多くの場合、`ArrayBuffer` を隠したまま、直接型付き配列を作成し操作します。`.buffer` でアクセスすることができ、必要に応じて別のビューを作成することができます。 + +その他に2つの用語があります: +- `ArrayBufferView` はこれらすべての種類のビューの総称です。 +- `BufferSource` は `ArrayBuffer` もしくは `ArrayBufferView` の総称です。 + +これらはバイナリデータを操作するメソッドの説明で使用されています。`BufferSource` は最も一般的な用語で、"あらゆる種類のバイナリデータ" -- `ArrayBuffer` またはそれを覆うビュー、を意味します。 + +チートシートです: + +![](arraybuffer-view-buffersource.svg) diff --git a/4-binary/02-text-decoder/article.md b/4-binary/02-text-decoder/article.md new file mode 100644 index 0000000000..6ca600c368 --- /dev/null +++ b/4-binary/02-text-decoder/article.md @@ -0,0 +1,76 @@ +# TextDecoder と TextEncoder + +もしバイナリデータが実際には文字列だったらどうしますか?例えばテキストデータを持つファイルを受け取りました。 + +組み込みの [TextDecoder](https://encoding.spec.whatwg.org/#interface-textdecoder) オブジェクトを使うと、指定されたバッファとエンコーディングから、値を実際の JavaScript 文字列として読むことができます。 + +最初に TextDecoder を作成する必要があります: +```js +let decoder = new TextDecoder([label], [options]); +``` + +- **`label`** -- エンコーディング、デフォルトでは `utf-8` ですが、`big5`, `windows-1251` やその他多くをサポートしています。 +- **`options`** -- オプションのオブジェクト: + - **`fatal`** -- boolean, `true` の場合、無効(デコード不可能)な文字の場合は例外をスローし、そうでなければ(デフォルト)、それらを `\uFFFD` に置き換えます。 + - **`ignoreBOM`** -- boolean, `true` の場合、BOM (オプションのバイトオーダーマーク)を無視します。めったに使われません。 + +...そしてデコードします: + +```js +let str = decoder.decode([input], [options]); +``` + +- **`input`** -- デコード対象の `BufferSource` です +- **`options`** -- オプションのオブジェクト: + - **`stream`** -- `decoder` が入ってくるデータのチャンクに対し繰り返し呼び出されるとき、ストリームをデコードする場合は `true` です。この場合、マルチバイト文字がチャンク間で分割される可能性があります。このオプションは "未完了" の文字を記憶して、次のチャンクが来た時にそれらをデコードするように `TextDecoder` に伝えます。 + +例: + +```js run +let uint8Array = new Uint8Array([72, 101, 108, 108, 111]); + +alert( new TextDecoder().decode(uint8Array) ); // Hello +``` + + +```js run +let uint8Array = new Uint8Array([228, 189, 160, 229, 165, 189]); + +alert( new TextDecoder().decode(uint8Array) ); // 你好 +``` + +サブアレイ(subarray)ビューを作成することで、バッファの一部をデコードすることもできます: + + +```js run +let uint8Array = new Uint8Array([0, 72, 101, 108, 108, 111, 0]); + +// 文字列が中央にあります +// コピーせずに、新しいビューを作成します +let binaryString = uint8Array.subarray(1, -1); + +alert( new TextDecoder().decode(binaryString) ); // Hello +``` + +## TextEncoder + +[TextEncoder](https://encoding.spec.whatwg.org/#interface-textencoder) は逆のこと -- 文字列をバイトにします。 + +構文は次の通りです: + +```js run +let encoder = new TextEncoder(); +``` + +サポートしているエンコーディングは "utf-8" だけです。 + +2つのメソッドがあります: +- **`encode(str)`** -- 文字列から `Uint8Array` を返します。 +- **`encodeInto(str, destination)`** -- `str` を `destination` にエンコードします。`destination` は `Uint8Array` でなければなりません。 + +```js run +let encoder = new TextEncoder(); + +let uint8Array = encoder.encode("Hello"); +alert(uint8Array); // 72,101,108,108,111 +``` diff --git a/4-binary/03-blob/article.md b/4-binary/03-blob/article.md new file mode 100644 index 0000000000..f379bb9106 --- /dev/null +++ b/4-binary/03-blob/article.md @@ -0,0 +1,240 @@ +# Blob + +`ArrayBuffer` とビューは ECMA 規格、JavaScript の一部です。 + +ブラウザには、さらに [File API](https://www.w3.org/TR/FileAPI/) に記載されている高水準のオブジェクトがあります。特に `Blob` です。 + +`Blob` はオプションの文字列 `type` (通常は MIMEタイプ) と `blobParts` -- 一連の他の `Blob` オブジェクト、文字列や `BufferSources` から構成されます。 + +![](blob.svg) + +コンストラクタの構文は次の通りです: + +```js +new Blob(blobParts, options); +``` + +- **`blobParts`** は `Blob`/`BufferSource`/`String` の値の配列です +- **`options`** オプションのオブジェクト: + - **`type`** -- blob タイプ, 通常は例えば `image/png` のような MIME タイプです, + - **`endings`** -- blob が現在の OS の改行(`\r\n` or `\n`)に対応するように行末を変換するかどうかを意味します。デフォルトでは `"transparent"` (何もしません)ですが、 `"native"` (変換する)にすることもできます。 + +例: + +```js +// 文字列から Blob を作成します +let blob = new Blob(["<html>…</html>"], {type: 'text/html'}); +// 注意してください: 最初の引数は配列である必要があります +``` + +```js +// 型付き配列と文字列から Blob を作成します +let hello = new Uint8Array([72, 101, 108, 108, 111]); // 二進での "hello" + +let blob = new Blob([hello, ' ', 'world'], {type: 'text/plain'}); +``` + + +blob の一部を抽出することもできます: + +```js +blob.slice([byteStart], [byteEnd], [contentType]); +``` + +- **`byteStart`** -- 開始バイト、デフォルトは 0。 +- **`byteEnd`** -- 最後のバイト(このバイトは含みません。デフォルトは最後までです). +- **`contentType`** -- 新しい blob の `type` です。デフォルトは元と同じになります。 + +引数は `array.slice` に似ており、負の値も許可されます。 + +```smart header="Blobs は immutable(イミュータブル(不変))です" +blob のデータを直接変更することはできませんが、blob の一部を切り出したり、それらから新しい blob を作成したり、それらを新しい blob にミックスしたりすることはできます。 + +この振る舞いは JavaScript の文字列に似ています: 文字列内の文字を変えることはできませんが、訂正した新しい文字列を作成することはできます。 +``` + +## URL としての Blob + +Blob はその内容を表示するのに、`<a>`, `<img>` や他のタグの URL として簡単に使うことができます。 + +`type` のおかげで blob をダウンロード/アップロードすることも可能で、それはネットワークリクエストではもちろん `Content-Type` になります。 + +簡単例から始めてみましょう。リンクをクリックすると、`hello world` の内容をもつ、動的に生成された blob をファイルとしてダウンロードします。: + +```html run +<!-- download 属性は、ブラウザは移動する代わりにダウンロードを行います --> +<a download="hello.txt" href='#' id="link">Download</a> + +<script> +let blob = new Blob(["Hello, world!"], {type: 'text/plain'}); + +link.href = URL.createObjectURL(blob); +</script> +``` + +また、JavaScript で動的にリンクを作成し、`link.click()` によりクリックをシミュレートすることもできます。すると、ダウンロードは自動的に始まります。 + +これは HTML なしで、動的に生成された Blob を利用者にダウンロードさせる類似のコードです: + +```js run +let link = document.createElement('a'); +link.download = 'hello.txt'; + +let blob = new Blob(['Hello, world!'], {type: 'text/plain'}); + +link.href = URL.createObjectURL(blob); + +link.click(); + +URL.revokeObjectURL(link.href); +``` + +`URL.createObjectURL` は blob を取り、`blob:<origin>/<uuid>` という形式の一意なURLを生成します。 + +これは、`link.href` の値がどのように見えるかの例です: + +``` +blob:https://javascript.info/1e67e00e-860d-40a5-89ae-6ab0cbee6273 +``` + +`URL.createObjectURL` により生成された各 URL に対して、ブラウザは url -> blob のマッピングを内部に格納します。そのため、url は短いですが、blob にアクセスすることができます。 + +生成された url (とそれへのリンク)は現在のドキュメント内で、かつ開いている間のみ有効です。それにより、`<img>`, `<a>` 、基本的に url を想定するあらゆるオブジェクトで blob を参照することができます。 + +しかし、副作用もあります。blob のマッピングがある間、blob 自身はメモリ内に存在し続けます。ブラウザはそれを解放することはできません。 + +マッピングは、ドキュメントがアンロードされると自動的にクリアされ、blob もそのとき解放されます。しかしアプリケーションの寿命が長ければ、すぐにはそれは起きません。 + +**したがって、URL を作成すると、それ以上必要なくなっても、blob はメモリに溜まります。** + +`URL.revokeObjectURL(url)` は内部のマッピングから参照を削除します。これにより blob が削除され(他に参照がない場合)、メモリを解放することができます。 + +最後の例では、blob は即時ダウンロードのために、一度だけ使われることを意図しているので、すぐに `URL.revokeObjectURL(link.href)` を呼び出します。 + +しかし、クリック可能な HTML リンクのある前の例では、`URL.revokeObjectURL(link.href)` を呼び出しません。なぜなら、これは blob url を無効にするからです。失効後は、マッピングが削除されているので、url は機能しなくなります。 + +## Blob を base64 にする + +`URL.createObjectURL` の代替は blob を base64 エンコードされた文字列に変換する方法です。 + +このエンコーディングは、0 から 64 までの ASCII コードを用いて、バイナリデータを安全で "読み出し可能な" 文字列として表現します。そしてより重要なことは、"data-url" でこのエンコーディングが使用できることです。 + +[data url](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) は `data:[<mediatype>][;base64],<data>` の形式です。このような url は "通常の" url と同等に、あらゆる場所で使用することができます。 + +例えば、これはスマイリーです: + +```html +<img src="data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7"> +``` + +ブラウザは文字列をデコードし、イメージを表示します: <img src="data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7"> + +blob を base64 に変換するためには、組み込みの `FileReader` オブジェクトを使用します。それは複数のフォーマットで blob からデータを読むことができます。[次のチャプター](info:file)では、より詳しく説明します。 + +これは、blob をダウンロードするで、base64 経由です: + +```js run +let link = document.createElement('a'); +link.download = 'hello.txt'; + +let blob = new Blob(['Hello, world!'], {type: 'text/plain'}); + +*!* +let reader = new FileReader(); +reader.readAsDataURL(blob); // blob を base64 へ変換し onload を呼び出します +*/!* + +reader.onload = function() { + link.href = reader.result; // data url + link.click(); +}; +``` + +blob の URL を作成する方法は両方とも使用可能です。しかし、通常は `URL.createObjectURL(blob)` がよりシンプルで速いです。 + +```compare title-plus="URL.createObjectURL(blob)" title-minus="Blob to data url" ++ メモリを気にする場合は、無効にする必要があります。 ++ blob への直接アクセス、"encoding/decoding" はありません。 +- 何も無効にする必要はありません。 +- 巨大な blob のエンコーディングでは、パフォーマンスとメモリを失います。 +``` + +## Image を blob にする + +画像、画像の一部、あるいはページのスクリーンショットの blob を作成することもできます。どこかにアップロードするときに便利です。 + +画像操作は `<canvas>` 要素を通して行われます: + +1. [canvas.drawImage](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage) を使って、canvas 上で画像(または一部)を書きます。 +2. blob を作成し、完了時に `callback` を実行する canvas メソッド [.toBlob(callback, format, quality)](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob) を呼び出します。 + +下記の例では、画像を単にコピーしていますが、blob を作成する間に canvas 上で切り取ったり変形させることができます。: + +```js run +// 画像を取ります +let img = document.querySelector('img'); + +// 同じサイズの <canvas> を作ります +let canvas = document.createElement('canvas'); +canvas.width = img.clientWidth; +canvas.height = img.clientHeight; + +let context = canvas.getContext('2d'); + +// 画像をコピーします (このメソッドで画像をカットすることができます) +context.drawImage(img, 0, 0); +// canvas 上では、context.rotate(), やその他様々なことができます + +// toBlob は非同期操作で、callback は完了時に呼ばれます +canvas.toBlob(function(blob) { + // blob の準備ができたのでダウンロードします + let link = document.createElement('a'); + link.download = 'example.png'; + + link.href = URL.createObjectURL(blob); + link.click(); + + // ブラウザがメモリをクリアできるよう、内部の blob への参照を削除します。 + URL.revokeObjectURL(link.href); +}, 'image/png'); +``` + +コールバックより `async/await` を好む場合は次のようになります: +```js +let blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png')); +``` + +ページをスクリーンショットするには、<https://github.com/niklasvh/html2canvas> のようなライブラリが使えます。これがしていることは、単にページを見て `<canvas>` 上にそれを描いているだけです。そして、上と同じ方法でその blob を取得することができます。 + +## Blob から ArrayBuffer へ + +`Blob` コンストラクタは、`BufferSource` を含め、ほぼ何からでも blob を作成することができます。 + +しかし、低レベルの処理を実行する必要がある場合、`FileReader` を使って、最も低レベルである `ArrayBuffer` を取得することもできます。: + +```js +// blob から arrayBuffer を取得 +let fileReader = new FileReader(); + +*!* +fileReader.readAsArrayBuffer(blob); +*/!* + +fileReader.onload = function(event) { + let arrayBuffer = fileReader.result; +}; +``` + + +## サマリ + +`ArrayBuffer`, `Uint8Array` やその他の `BufferSource` が "バイナリデータ" である一方、[Blob](https://www.w3.org/TR/FileAPI/#dfn-Blob) は "タイプを持つバイナリデータ" を表します。 + +これは Blob のアップロード/ダウンロード操作を便利にし、ブラウザでは一般的です。 + +[XMLHttpRequest](info:xmlhttprequest), [fetch](info:fetch-basics) などの webリクエストを行うメソッドは、他のバイナリタイプと同様に、`Blob` をネイティブに使用することができます。 + +`Blob` と低レベルのバイナリデータ型の間の変換は容易に行うことができます。: + +- `new Blob(...)` コンストラクタを使用して、型付き配列から Blob を作成することができます。 +- `FileReader` を使用して、Blob から `ArrayBuffer` に戻すことができ、低レベルのバイナリ処理のためにビューを作成することもできます。 diff --git a/4-binary/03-blob/blob.svg b/4-binary/03-blob/blob.svg new file mode 100644 index 0000000000..8f42454516 --- /dev/null +++ b/4-binary/03-blob/blob.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="659" height="111" viewBox="0 0 659 111"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="binary" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="blob.svg"><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M108 56h96v28h-96z"/><text id="image/png" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="113.3" y="74">image/png</tspan></text><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M238 56h71v28h-71z"/><text id="blob1" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="250" y="74">blob1</tspan></text><path id="Rectangle-227-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M308 56h71v28h-71z"/><text id="blob2" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="320" y="74">blob2</tspan></text><path id="Rectangle-227-Copy-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M475 56h71v28h-71z"/><text id="str" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="497.1" y="74">str</tspan></text><path id="Rectangle-227-Copy-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M546 56h71v28h-71z"/><text id="buffer" fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="553.7" y="74">buffer</tspan></text><path id="Rectangle-227-Copy-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M379 56h96v28h-96z"/><text id="..." fill="#181717" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="413.1" y="74">...</tspan></text><text id="type" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="20" font-weight="normal"><tspan x="135.692" y="24">type</tspan></text><text id="Blob" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="20" font-weight="normal"><tspan x="24.823" y="76">Blob</tspan></text><text id="blobParts" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="20" font-weight="normal"><tspan x="378.21" y="24">blobParts</tspan></text><text id="+" fill="#A7333A" font-family="OpenSans-Regular, Open Sans" font-size="20" font-weight="normal"><tspan x="216.782" y="77">+</tspan></text><text id="=" fill="#A7333A" font-family="OpenSans-Regular, Open Sans" font-size="20" font-weight="normal"><tspan x="77.782" y="77">=</tspan></text><path id="Line-2" stroke="#C06334" stroke-linecap="square" stroke-width="2" d="M239 45c81.34-6.667 143.674-10 187-10 43.326 0 105.66 3.333 187 10"/></g></g></svg> \ No newline at end of file diff --git a/4-binary/04-file/article.md b/4-binary/04-file/article.md new file mode 100644 index 0000000000..d6a323533a --- /dev/null +++ b/4-binary/04-file/article.md @@ -0,0 +1,128 @@ +# File と FileReader + +[File](https://www.w3.org/TR/FileAPI/#dfn-file) オブジェクトは `Blob` を継承しており、ファイルシステムに関連した機能が拡張されています。 + +`File` の入手方法は2つあります。 + +1つ目は、`Blob` に似たコンストラクタを用いる方法です: + +```js +new File(fileParts, fileName, [options]) +``` + +- **`fileParts`** -- `Blob` 同様、Blob/BufferSource/文字列値 の配列です +- **`fileName`** -- ファイル名(文字列)です +- **`options`** -- オプションのオブジェクトです: + - **`lastModified`** -- 最後に変更された日付のタイムスタンプです(整数値)。 + +2つ目は、`<input type="file">` やドラッグドロップ、あるいは他のブラウザインタフェースを使用して、ファイルを取得する方法です(こちらの方が多いです)。 + +例: + +```html run +<input type="file" onchange="showFile(this)"> + +<script> +function showFile(input) { + let file = input.files[0]; + + alert(`File name: ${file.name}`); // e.g my.png + alert(`Last modified: ${file.lastModified}`); // e.g 1552830408824 +} +</script> +``` + +```smart +入力は複数のファイルを選択する可能性があるので、`input.files` は配列ライクなオブジェクトになります。ここは1つのファイルしかないので、単に `input.files[0]` としています。 +``` + +## FileReader + +[FileReader](https://www.w3.org/TR/FileAPI/#dfn-filereader) は `Blob` (なので `File` も)オブジェクトからデータを読み込むことのみを目的としたオブジェクトです。 + +ディスクからの読み込みは時間がかかる場合があるので、イベントを使用してデータを渡します。 + +コンストラクタです: + +```js +let reader = new FileReader(); // 引数はありません +``` + +主なメソッド: + +- **`readAsArrayBuffer(blob)`** -- `ArrayBuffer` としてデータを読み込みます +- **`readAsText(blob, [encoding])`** -- 文字列(デフォルトではエンコーディングは `utf-8` です)としてデータを読み込みます +- **`readAsDataURL(blob)`** -- データを base64 データurl にエンコードします +- **`abort()`** -- 操作をキャンセルします + +読み込み処理には、次のイベントがあります: +- `loadstart` -- ロード開始 +- `progress` -- 読み取り中 +- `load` -- エラーなく読み取り完了 +- `abort` -- `abort()` キャンセルされた +- `error` -- エラーが発生した +- `loadend` -- 読み込みが成功または失敗で終了 + +読み込みが終了すると、結果にアクセスできます: +- `reader.result` は結果です(成功している場合) +- `reader.error` はエラーです(失敗した場合) + +広く使われているイベントは `load` と `error` です。 + +これはファイル読み込みの例です: + +```html run +<input type="file" onchange="readFile(this)"> + +<script> +function readFile(input) { + let file = input.files[0]; + + let reader = new FileReader(); + + reader.readAsText(file); + + reader.onload = function() { + console.log(reader.result); + }; + + reader.onerror = function() { + console.log(reader.error); + }; + +} +</script> +``` + +```smart header="`FileReader` for blobs" +チャプター <info:blob> で述べたように、`FileReader` はファイルだけでなく、あらゆる blob に対しても動作します。 + +そのため、blob を別の形式に変換するのに使うこともできます: +- `readAsArrayBuffer(blob)` -- `ArrayBuffer` へ, +- `readAsText(blob, [encoding])` -- 文字列へ(`TextDecoder` の代替), +- `readAsDataURL(blob)` -- base64 url へ +``` + + +```smart header="`FileReaderSync` は worker でのみ利用可能です" +Web worker(Webワーカー)の場合、[FileReaderSync](https://www.w3.org/TR/FileAPI/#FileReaderSync) と呼ばれる、`FileReader` の同期版も存在します。 + +その読み込みメソッド `read*` はイベントを生成しませんが、通常の関数と同じように結果を返します。 + +これは Web worker 内でのみ可能です。なぜなら、Web worker では、ファイルの読み取り中に発生する可能性がある、同期呼び出しの遅延はたいして重要ではないからです。これらはページに影響を与えません。 +``` + +## サマリ + +`File` オブジェクトは `Blob` を継承しています。 + +`Blob` のメソッドとプロパティに加え、`File` オブジェクトは `fileName` と `lastModified` プロパティも持っており、さらにファイルシステムから読み込むための内部的な機能もあります。通常 `File` オブジェクトは `<input>` やドラッグドロップのようなユーザ操作から取得します。 + +`FileReader` オブジェクトは次の3つのフォーマットで、ファイルや blob を読み込むことができます。: +- 文字列 (`readAsText`). +- `ArrayBuffer` (`readAsArrayBuffer`). +- データ url, base-64 エンコード (`readAsDataURL`). + +しかし、多くのケースではファイルの内容を読み込む必要はありません。blob でやったのと同じように、`URL.createObjectURL(file)` で短い url を作成し、それを `<a>` や `<img>` に割り当てることができます。これにより、ファイルをダウンロードしたり、画像や、canvas などの一部として表示させることができます。 + +また、ネットワーク越しに `File` を送るつもりだとしても、`XMLHttpRequest` や `fetch` のようなネットワークAPIはネイティブに `File` オブジェクトを受け入れるので簡単に行うことができます。 diff --git a/4-binary/index.md b/4-binary/index.md new file mode 100644 index 0000000000..64a679b4a4 --- /dev/null +++ b/4-binary/index.md @@ -0,0 +1,3 @@ +# バイナリデータ、ファイル + +JavaScript でバイナリデータやファイルを扱います。 diff --git a/4-frames-and-windows/01-popup-windows/article.md b/4-frames-and-windows/01-popup-windows/article.md deleted file mode 100644 index a1839d6a7c..0000000000 --- a/4-frames-and-windows/01-popup-windows/article.md +++ /dev/null @@ -1,193 +0,0 @@ -# Popups and window methods - -A popup window is one of the oldest methods to show additional document to user. - -Basically, you just run: -```js -window.open('http://javascript.info/') -``` - -... And it will open a new window with given URL. Most modern browsers are configured to open new tabs instead of separate windows. - -[cut] - -## Popup blocking - -Popups exist from really ancient times. The initial idea was to show another content without closing the main window. As of now, there are other ways to do that: JavaScript is able to send requests for server, so popups are rarely used. But sometimes they are still handy. - -In the past evil sites abused popups a lot. A bad page could open tons of popup windows with ads. So now most browsers try to block popups and protect the user. - -**Most browsers block popups if they are called outside of user-triggered event handlers like `onclick`.** - -If you think about it, that's a bit tricky. If the code is directly in an `onclick` handler, then that's easy. But what is the popup opens in `setTimeout`? - -Try this code: - -```js run -// open after 3 seconds -setTimeout(() => window.open('http://google.com'), 3000); -``` - -The popup opens in Chrome, but gets blocked in Firefox. - -...And this works in Firefox too: - -```js run -// open after 1 seconds -setTimeout(() => window.open('http://google.com'), 1000); -``` - -The difference is that Firefox treats a timeout of 2000ms or less are acceptable, but after it -- removes the "trust", assuming that now it's "outside of the user action". So the first one is blocked, and the second one is not. - -## Modern usage - -As of now, we have many methods to load and show data on-page with JavaScript. But there are still situations when a popup works best. - -For instance, many shops use online chats for consulting people. A visitor clicks on the button, it runs `window.open` and opens the popup with the chat. - -Why a popup is good here, why not in-page? - -1. A popup is a separate window with its own independent JavaScript environment. So a chat service doesn't need to integrate with scripts of the main shop site. -2. A popup is very simple to attach to a site, little to no overhead. It's only a small button, without additional scripts. -3. A popup may persist even if the user left the page. For example, a consult advices the user to visit the page of a new "Super-Cooler" goodie. The user goes there in the main window without leaving the chat. - -## window.open - -The syntax to open a popup is: `window.open(url, name, params)`: - -url -: An URL to load into the new window. - -name -: A name of the new window. Each window has a `window.name`, and here we can specify which window to use for the popup. If there's already a window with such name -- the given URL opens in it, otherwise a new window is opened. - -params -: The configuration string for the new window. It contains settings, delimited by a comma. There must be no spaces in params, for instance: `width:200,height=100`. - -Settings for `params`: - -- Position: - - `left/top` (numeric) -- coordinates of the window top-left corner on the screen. There is a limitation: a new window cannot be positioned offscreen. - - `width/height` (numeric) -- width and height of a new window. There is a limit on minimal width/height, so it's impossible to create an invisible window. -- Window features: - - `menubar` (yes/no) -- shows or hides the browser menu on the new window. - - `toolbar` (yes/no) -- shows or hides the browser navigation bar (back, forward, reload etc) on the new window. - - `location` (yes/no) -- shows or hides the URL field in the new window. FF and IE don't allow to hide it by default. - - `status` (yes/no) -- shows or hides the status bar. Again, most browsers force it to show. - - `resizable` (yes/no) -- allows to disable the resize for the new window. Not recommended. - - `scrollbars` (yes/no) -- allows to disable the scrollbars for the new window. Not recommended. - - -There is also a number of less supported browser-specific features, which are usually not used. Check <a href="https://developer.mozilla.org/en/DOM/window.open">window.open in MDN</a> for examples. - -## Example: a minimalistic window - -Let's open a window with minimal set of features just to see which of them browser allows to disable: - -```js run -let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no, -width=0,height=0,left=-1000,top=-1000`; - -open('/', 'test', params); -``` - -Here most "window features" are disabled and window is positioned offscreen. Run it and see what really happens. Most browsers "fix" odd things like zero `width/height` and offscreen `left/top`. For instance, Chrome open such a window with full width/height, so that it occupies the full screen. - -Let's add normal positioning options and reasonable `width`, `height`, `left`, `top` coordinates: - -```js run -let params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no, -width=600,height=300,left=100,top=100`; - -open('/', 'test', params); -``` - -Most browsers show the example above as required. - -Rules for omitted settings: - -- If there is no 3rd argument in the `open` call, or it is empty, then the default window parameters are used. -- If there is a string of params, but some yes/no features are omitted, then the omitted features are disabled, if the browser allows that. So if you specify params, make sure you explicitly set all required features to yes. -- If there is no `left/top` in params, then the browser tries to open a new window near the last opened window. -- If there is no `width/height`, then the new window will be the same size as the last opened. - -## Accessing a popup - -The `open` call returns a reference to the new window. It can be used to manipulate it's properties, change location and even more. - -In the example below, the contents of the new window is modified after loading. - -```js run -let newWindow = open('/', 'example', 'width=300,height=300') -newWindow.focus(); - -newWindow.onload = function() { - let html = `<div style="font-size:30px">Welcome!</div>`; -*!* - newWindow.document.body.insertAdjacentHTML('afterbegin', html); -*/!* -}; -``` - -Please note that external `document` content is only accessible for windows from the same origin (the same protocol://domain:port). - -For windows with URLs from another sites, we are able to change the location by assigning `newWindow.location=...`, but we can't read the location or access the content. That's for user safety, so that an evil page can't open a popup with `http://gmail.com` and read the data. We'll talk more about it later. - -## Accessing the opener window - -A popup may access the "opener" window as well. A JavaScript in it may use `window.opener` to access the window that opened it. It is `null` for all windows except popups. - -So both the main window and the popup have a reference to each other. They may modify each other freely assuming that they come from the same origin. If that's not so, then there are still means to communicate, to be covered in the next chapter <info:cross-window-communication>. - -## Closing a popup - -If we don't need a popup any more, we can call `newWindow.close()` on it. - -Technically, the `close()` method is available for any `window`, but `window.close()` is ignored by most browsers if `window` is not created with `window.open()`. - -The `newWindow.closed` is `true` if the window is closed. That's useful to check if the popup (or the main window) is still open or not. A user could close it, and our code should take that possibility into account. - -This code loads and then closes the window: - -```js run -let newWindow = open('/', 'example', 'width=300,height=300') -newWindow.onload = function() { - newWindow.close(); - alert(newWindow.closed); // true -}; -``` - -## Focus/blur on a popup - -Theoretically, there are `window.focus()` and `window.blur()` methods to focus/unfocus on a window. Also there are `focus/blur` events that allow to focus a window and catch the moment when the visitor switches elsewhere. - -In the past evil pages abused those. For instance, look at this code: - -```js run -window.onblur = () => window.focus(); -``` - -When a user attempts to switch out of the window (`blur`), it brings it back to focus. The intention is to "lock" the user within the `window`. - -So, there are limitations that forbid the code like that. There are many limitations to protect the user from ads and evils pages. They depend on the browser. - -For instance, a mobile browser usually ignores that call completely. Also focusing doesn't work when a popup opens in a separate tab rather than a new window. - -Still, there are some things that can be done. - -For instance: - -- When we open a popup, it's might be a good idea to run a `newWindow.focus()` on it. Just in case, for some OS/browser combinations it ensures that the user is in the new window now. -- If we want to track when a visitor actually uses our web-app, we can track `window.onfocus/onblur`. That allows us to suspend/resume in-page activities, animations etc. But please note that the `blur` event means that the visitor switched out from the window, but he still may observe it. The window is in the background, but still may be visible. - -## Summary - -- A popup can be opened by the `open(url, name, params)` call. It returns the reference to the newly opened window. -- By default, browsers block `open` calls from the code outside of user actions. Usually a notification appears, so that a user may allow them. -- The popup may access the opener window using the `window.opener` property, so the two are connected. -- If the main window and the popup come from the same origin, they can freely read and modify each other. Otherwise, they can change location of each other and communicate using messages (to be covered). -- To close the popup: use `close()` call. Also the user may close them (just like any other windows). The `window.closed` is `true` after that. -- Methods `focus()` and `blur()` allow to focus/unfocus a window. Sometimes. -- Events `focus` and `blur` allow to track switching in and out of the window. But please note that a window may still be visible even in the background state, after `blur`. - -Also if we open a popup, a good practice is to notify the user about it. An icon with the opening window can help the visitor to survive the focus shift and keep both windows in mind. diff --git a/4-frames-and-windows/03-cross-window-communication/article.md b/4-frames-and-windows/03-cross-window-communication/article.md deleted file mode 100644 index 6d6ffcd0cc..0000000000 --- a/4-frames-and-windows/03-cross-window-communication/article.md +++ /dev/null @@ -1,367 +0,0 @@ -# Cross-window communication - -The "Same Origin" (same site) policy limits access of windows and frame to each other. - -The idea is that if we have two windows open: one from `john-smith.com`, and another one is `gmail.com`, then we wouldn't want a script from `john-smith.com` to read our mail. - -[cut] - -## Same Origin - -Two URLs are said to have the "same origin" if they have the same protocol, domain and port. - -These URLs all share the same origin: - -- `http://site.com` -- `http://site.com/` -- `http://site.com/my/page.html` - -These ones do not: - -- <code>http://<b>www.</b>site.com</code> (another domain: `www.` matters) -- <code>http://<b>site.org</b></code> (another domain: `.org` matters) -- <code><b>https://</b>site.com</code> (another protocol: `https`) -- <code>http://site.com:<b>8080</b></code> (another port: `8080`) - -If we have a reference to another window (a popup or iframe), and that window comes from the same origin, then we can do everything with it. - -If it comes from another origin, then we can only change its location. Please note: not *read* the location, but *modify* it, redirect it to another place. That's safe, because the URL may contain sensitive parameters, so reading it from another origin is prohibited, but changing is not. - -Also such windows may exchange messages. Soon about that later. - -````warn header="Exclusion: subdomains may be same-origin" - -There's an important exclusion in the same-origin policy. - -If windows share the same second-level domain, for instance `john.site.com`, `peter.site.com` and `site.com`, we can use JavaScript to assign to `document.domain` their common second-level domain `site.com`. Then these windows are treated as having the same origin. - -In other words, all such documents (including the one from `site.com`) should have the code: - -```js -document.domain = 'site.com'; -``` - -Then they can interact without limitations. - -That's only possible for pages with the same second-level domain. -```` - -## Accessing an iframe contents - -An `<iframe>` is a two-faced beast. From one side it's a tag, just like `<script>` or `<img>`. From the other side it's a window-in-window. - -The embedded window has a separate `document` and `window` objects. - -We can access them like using the properties: - -- `iframe.contentWindow` is a reference to the window inside the `<iframe>`. -- `iframe.contentDocument` is a reference to the document inside the `<iframe>`. - -When we access an embedded window, the browser checks if the iframe has the same origin. If that's not so then the access is denied (with exclusions noted above). - -For instance, here's an `<iframe>` from another origin: - -```html run -<iframe src="https://example.com" id="iframe"></iframe> - -<script> - iframe.onload = function() { - // we can get the reference to the inner window - let iframeWindow = iframe.contentWindow; - - try { - // ...but not to the document inside it - let doc = iframe.contentDocument; - } catch(e) { - alert(e); // Security Error (another origin) - } - - // also we can't read the URL of the page in it - try { - alert(iframe.contentWindow.location); - } catch(e) { - alert(e); // Security Error - } - - // ...but we can change it (and thus load something else into the iframe)! - iframe.contentWindow.location = '/'; // works - - iframe.onload = null; // clear the handler, to run this code only once - }; -</script> -``` - -The code above shows errors for any operations except: - -- Getting the reference to the inner window `iframe.contentWindow` -- Changing its `location`. - -```smart header="`iframe.onload` vs `iframe.contentWindow.onload`" -The `iframe.onload` event is actually the same as `iframe.contentWindow.onload`. It triggers when the embedded window fully loads with all resources. - -...But `iframe.onload` is always available, while `iframe.contentWindow.onload` needs the same origin. -``` - -And now an example with the same origin. We can do anything with the embedded window: - -```html run -<iframe src="/" id="iframe"></iframe> - -<script> - iframe.onload = function() { - // just do anything - iframe.contentDocument.body.prepend("Hello, world!"); - }; -</script> -``` - -### Please wait until the iframe loads - -When an iframe is created, it immediately has a document. But that document is different from the one that finally loads into it! - -Here, look: - - -```html run -<iframe src="/" id="iframe"></iframe> - -<script> - let oldDoc = iframe.contentDocument; - iframe.onload = function() { - let newDoc = iframe.contentDocument; -*!* - // the loaded document is not the same as initial! - alert(oldDoc == newDoc); // false -*/!* - }; -</script> -``` - -That's actually a well-known pitfall for novice developers. We shouldn't work with the document immediately, because that's the *wrong document*. If we set any event handlers on it, they will be ignored. - -...But the `onload` event triggers when the whole iframe with all resources is loaded. What if we want to act sooner, on `DOMContentLoaded` of the embedded document? - -That's not possible if the iframe comes from another origin. But for the same origin we can try to catch the moment when a new document appears, and then setup necessary handlers, like this: - -```html run -<iframe src="/" id="iframe"></iframe> - -<script> - let oldDoc = iframe.contentDocument; - - // every 100 ms check if the document is the new one - let timer = setInterval(() => { - if (iframe.contentDocument == oldDoc) return; - - // new document, let's set handlers - iframe.contentDocument.addEventListener('DOMContentLoaded', () => { - iframe.contentDocument.body.prepend('Hello, world!'); - }); - - clearInterval(timer); // cancel setInterval, don't need it any more - }, 100); -</script> -``` - -Let me know in comments if you know a better solution here. - -## window.frames - -An alternative way to get a window object for `<iframe>` -- is to get it from the named collection `window.frames`: - -- By number: `window.frames[0]` -- the window object for the first frame in the document. -- By name: `window.frames.iframeName` -- the window object for the frame with `name="iframeName"`. - -For instance: - -```html run -<iframe src="/" style="height:80px" name="win" id="iframe"></iframe> - -<script> - alert(iframe.contentWindow == frames[0]); // true - alert(iframe.contentWindow == frames.win); // true -</script> -``` - -An iframe may have other iframes inside. The corresponding `window` objects form a hierarchy. - -Navigation links are: - -- `window.frames` -- the collection of "children" windows (for nested frames). -- `window.parent` -- the reference to the "parent" (outer) window. -- `window.top` -- the reference to the topmost parent window. - -For instance: - -```js run -window.frames[0].parent === window; // true -``` - -We can use the `top` property to check if the current document is open inside a frame or not: - -```js run -if (window == top) { // current window == window.top? - alert('The script is in the topmost window, not in a frame'); -} else { - alert('The script runs in a frame!'); -} -``` - -## The sandbox attribute - -The `sandbox` attribute allows to forbid certain actions inside an `<iframe>`, to run an untrusted code. It "sandboxes" the iframe by treating it as coming from another origin and/or applying other limitations. - -By default, for `<iframe sandbox src="...">` the "default set" of restrictions is applied to the iframe. But we can provide a space-separated list of "excluded" limitations as a value of the attribute, like this: `<iframe sandbox="allow-forms allow-popups">`. The listed limitations are not applied. - -In other words, an empty `"sandbox"` attribute puts the strictest limitations possible, but we can put a space-delimited list of those that we want to lift. - -Here's a list of limitations: - -`allow-same-origin` -: By default `"sandbox"` forces the "different origin" policy for the iframe. In other words, it makes the browser to treat the `iframe` as coming from another origin, even if its `src` points to the same site. With all implied restrictions for scripts. This option removes that feature. - -`allow-top-navigation` -: Allows the `iframe` to change `parent.location`. - -`allow-forms` -: Allows to submit forms from `iframe`. - -`allow-scripts` -: Allows to run scripts from the `iframe`. - -`allow-popups` -: Allows to `window.open` popups from the `iframe` - -See [the manual](mdn:/HTML/Element/iframe) for more. - -The example below demonstrates a sandboxed iframe with the default set of restrictions: `<iframe sandbox src="...">`. It has some JavaScript and a form. - -Please note that nothing works. So the default set is really harsh: - -[codetabs src="sandbox" height=140] - - -```smart -The purpose of the `"sandbox"` attribute is only to *add more* restrictions. It cannot remove them. In particular, it can't relax same-origin restrictions if the iframe comes from another origin. -``` - -## Cross-window messaging - -The `postMessage` interface allows windows to talk to each other no matter which origin they are from. - -It has two parts. - -### postMessage - -The window that wants to send a message calls [postMessage](mdn:api/Window.postMessage) method of the receiving window. In other words, if we want to send the message to `win`, we should call `win.postMessage(data, targetOrigin)`. - -Arguments: - -`data` -: The data to send. Can be any object, the data is cloned using the "structured cloning algorithm". IE supports only strings, so we should `JSON.stringify` complex objects to support that browser. - -`targetOrigin` -: Specifies the origin for the target window, so that only a window from the given origin will get the message. - -The `targetOrigin` is a safety measure. Remember, if the target window comes from another origin, we can't read it's `location`. So we can't be sure which site is open in the intended window right now: the user could navigate away. - -Specifying `targetOrigin` ensures that the window only receives the data if it's still at that site. Good when the data is sensitive. - -For instance, here `win` will only receive the message if it has a document from the origin `http://example.com`: - -```html no-beautify -<iframe src="http://example.com" name="example"> - -<script> - let win = window.frames.example; - - win.postMessage("message", "http://example.com"); -</script> -``` - -If we don't want that check, we can set `targetOrigin` to `*`. - -```html no-beautify -<iframe src="http://example.com" name="example"> - -<script> - let win = window.frames.example; - -*!* - win.postMessage("message", "*"); -*/!* -</script> -``` - - -### onmessage - -To receive a message, the target window should have a handler on the `message` event. It triggers when `postMessage` is called (and `targetOrigin` check is successful). - -The event object has special properties: - -`data` -: The data from `postMessage`. - -`origin` -: The origin of the sender, for instance `http://javascript.info`. - -`source` -: The reference to the sender window. We can immediately `postMessage` back if we want. - -To assign that handler, we should use `addEventListener`, a short syntax `window.onmessage` does not work. - -Here's an example: - -```js -window.addEventListener("message", function(event) { - if (event.origin != 'http://javascript.info') { - // something from an unknown domain, let's ignore it - return; - } - - alert( "received: " + event.data ); -}); -``` - -The full example: - -[codetabs src="postmessage" height=120] - -```smart header="There's no delay" -There's totally no delay between `postMessage` and the `message` event. That happens synchronously, even faster than `setTimeout(...,0)`. -``` - -## Summary - -To call methods and access the content of another window, we should first have a reference to it. - -For popups we have two properties: -- `window.open` -- opens a new window and returns a reference to it, -- `window.opener` -- a reference to the opener window from a popup - -For iframes, we can access parent/children windows using: -- `window.frames` -- a collection of nested window objects, -- `window.parent`, `window.top` are the references to parent and top windows, -- `iframe.contentWindow` is the window inside an `<iframe>` tag. - -If windows share the same origin (host, port, protocol), then windows can do whatever they want with each other. - -Otherwise, only possible actions are: -- Change the location of another window (write-only access). -- Post a message to it. - -Exclusions are: -- Windows that share the same second-level domain: `a.site.com` and `b.site.com`. Then setting `document.domain='site.com'` in both of them puts them into the "same origin" state. -- If an iframe has a `sandbox` attribute, it is forcefully put into the "different origin" state, unless the `allow-same-origin` is specified in the attribute value. That can be used to run untrusted code in iframes from the same site. - -The `postMessage` interface allows two windows to talk with security checks: - -1. The sender calls `targetWin.postMessage(data, targetOrigin)`. -2. If `targetOrigin` is not `'*'`, then the browser checks if window `targetWin` has the URL from `targetWin` site. -3. If it is so, then `targetWin` triggers the `message` event with special properties: - - `origin` -- the origin of the sender window (like `http://my.site.com`) - - `source` -- the reference to the sender window. - - `data` -- the data, any object in everywhere except IE that supports only strings. - - We should use `addEventListener` to set the handler for this event inside the target window. diff --git a/4-frames-and-windows/06-clickjacking/article.md b/4-frames-and-windows/06-clickjacking/article.md deleted file mode 100644 index 84d8f97d3a..0000000000 --- a/4-frames-and-windows/06-clickjacking/article.md +++ /dev/null @@ -1,202 +0,0 @@ -# The clickjacking attack - -The "clickjacking" attack allows an evil page to click on a "victim site" *on behalf of the visitor*. - -Many sites were hacked this way, including Twitter, Facebook, Paypal and other sites. They are all fixed, of course. - -## The idea - -The idea is very simple. - -Here's how clickjacking was done with Facebook: - -1. A visitor is lured to the evil page. It doesn't matter how. -2. The page has a harmless-looking link on it (like "get rich now" or "click here, very funny"). -3. Over that link the evil page positions a transparent `<iframe>` with `src` from facebook.com, in such a way that the "Like" button is right above that link. Usually that's done with `z-index`. -4. In attempting to click the link, the visitor in fact clicks the button. - -## The demo - -Here's how the evil page looks. To make things clear, the `<iframe>` is half-transparent (in real evil pages it's fully transparent): - -```html run height=120 no-beautify -<style> -iframe { /* iframe from the victim site */ - width: 400px; - height: 100px; - position: absolute; - top:0; left:-20px; -*!* - opacity: 0.5; /* in real opacity:0 */ -*/!* - z-index: 1; -} -</style> - -<div>Click to get rich now:</div> - -<!-- The url from the victim site --> -*!* -<iframe src="/clickjacking/facebook.html"></iframe> - -<button>Click here!</button> -*/!* - -<div>...And you're cool (I'm a cool hacker actually)!</div> -``` - -The full demo of the attack: - -[codetabs src="clickjacking-visible" height=160] - -Here we have a half-transparent `<iframe src="facebook.html">`, and in the example we can see it hovering over the button. A click on the button actually clicks on the iframe, but that's not visible to the user, because the iframe is transparent. - -As a result, if the visitor is authorized on Facebook ("remember me" is usually turned on), then it adds a "Like". On Twitter that would be a "Follow" button. - -Here's the same example, but closer to reality, with `opacity:0` for `<iframe>`: - -[codetabs src="clickjacking" height=160] - -All we need to attack -- is to position the `<iframe>` on the evil page in such a way that the button is right over the link. That's usually possible with CSS. - -```smart header="Clickjacking is for clicks, not for keyboard" -The attack only affects mouse actions. - -Technically, if we have a text field to hack, then we can position an iframe in such a way that text fields overlap each other. So when a visitor tries to focus on the input he sees on the page, he actually focuses on the input inside the iframe. - -But then there's a problem. Everything that the visitor types will be hidden, because the iframe is not visible. - -People will usually stop typing when they can't see their new characters printing on the screen. -``` - -## Old-school defences (weak) - -The oldest defence is a bit of JavaScript which forbids opening the page in a frame (so-called "framebusting"). - -That looks like this: - -```js -if (top != window) { - top.location = window.location; -} -``` - -That is: if the window finds out that it's not on top, then it automatically makes itself the top. - -This not a reliable defence, because there are many ways to hack around it. Let's cover a few. - -### Blocking top-navigation - -We can block the transition caused by changing `top.location` in the [beforeunload](info:onload-ondomcontentloaded#window.onbeforeunload) event. - -The top page (belonging to the hacker) sets a handler to it, and when the `iframe` tries to change `top.location` the visitor gets a message asking him whether he wants to leave. - -Like this: -```js -window.onbeforeunload = function() { - window.onbeforeunload = null; - return "Want to leave without learning all the secrets (he-he)?"; -}; -``` - -In most cases the visitor would answer negatively, because he doesn't know about the iframe, all he can see is the top page, leading him to think there is no reason to leave. So `top.location` won't change! - -In action: - -[codetabs src="top-location"] - -### Sandbox attribute - -One of the things restricted by the `sandbox` attribute is navigation. A sandboxed iframe may not change `top.location`. - -So we can add the iframe with `sandbox="allow-scripts allow-forms"`. That would relax the restrictions, permitting scripts and forms. But we omit `allow-top-navigation` so that changing `top.location` is forbidden. - -Here's the code: - -```html -<iframe *!*sandbox="allow-scripts allow-forms"*/!* src="facebook.html"></iframe> -``` - -There are other ways to work around that simple protection too. - -## X-Frame-Options - -The server-side header `X-Frame-Options` can permit or forbid displaying the page inside a frame. - -It must be sent *by the server*: the browser will ignore it if found in a `<meta>` tag. So, `<meta http-equiv="X-Frame-Options"...>` won't do anything. - -The header may have 3 values: - - -`DENY` -: Never ever show the page inside a frame. - -`SAMEORIGIN` -: Allow inside a frame if the parent document comes from the same origin. - -`ALLOW-FROM domain` -: Allow inside a frame if the parent document is from the given domain. - -For instance, Twitter uses `X-Frame-Options: SAMEORIGIN`. - -````online -Here's the result: - -```html -<iframe src="https://twitter.com"></iframe> -``` - -<iframe src="https://twitter.com"></iframe> - -Depending on your browser, the `iframe` above is either empty or alerting you that the browser won't permit that page to be navigating in this way. -```` - -## Showing with disabled functionality - -The `X-Frame-Options` header has a side-effect. Other sites won't be able to show our page in a frame, even if they have good reasons to do so. - -So there are other solutions... For instance, we can "cover" the page with a `<div>` with `height: 100%; width: 100%;`, so that it intercepts all clicks. That `<div>` should disappear if `window == top` or if we figure out that we don't need the protection. - -Something like this: - -```html -<style> - #protector { - height: 100%; - width: 100%; - position: absolute; - left: 0; - top: 0; - z-index: 99999999; - } -</style> - -<div id="protector"> - <a href="/" target="_blank">Go to the site</a> -</div> - -<script> - // there will be an error if top window is from the different origin - // but that's ok here - if (top.document.domain == document.domain) { - protector.remove(); - } -</script> -``` - -The demo: - -[codetabs src="protector"] - -## Summary - -Clickjacking is a way to "trick" users into clicking on a malicious site without even knowing what's happening. That's dangerous if there are important click-activated actions. - -A hacker can post a link to his evil page in a message, or lure visitors to his page by some other means. There are many variations. - -From one perspective -- the attack is "not deep": all a hacker is doing is intercepting a single click. But from another perspective, if the hacker knows that after the click another control will appear, then he may use cunning messages to coerce the user into clicking on them as well. - -The attack is quite dangerous, because when we engineer the UI we usually don't anticipate that a hacker may click on behalf of the visitor. So vulnerabilities can be found in totally unexpected places. - -- It is recommended to use `X-Frame-Options: SAMEORIGIN` on pages (or whole websites) which are not intended to be viewed inside frames. -- Use a covering `<div>` if we want to allow our pages to be shown in iframes, but still stay safe. diff --git a/5-network/01-fetch/01-fetch-users/_js.view/solution.js b/5-network/01-fetch/01-fetch-users/_js.view/solution.js new file mode 100644 index 0000000000..da448b47a1 --- /dev/null +++ b/5-network/01-fetch/01-fetch-users/_js.view/solution.js @@ -0,0 +1,24 @@ + +async function getUsers(names) { + let jobs = []; + + for(let name of names) { + let job = fetch(`https://api.github.com/users/${name}`).then( + successResponse => { + if (successResponse.status != 200) { + return null; + } else { + return successResponse.json(); + } + }, + failResponse => { + return null; + } + ); + jobs.push(job); + } + + let results = await Promise.all(jobs); + + return results; +} diff --git a/5-network/01-fetch/01-fetch-users/_js.view/source.js b/5-network/01-fetch/01-fetch-users/_js.view/source.js new file mode 100644 index 0000000000..0c62e7bb57 --- /dev/null +++ b/5-network/01-fetch/01-fetch-users/_js.view/source.js @@ -0,0 +1,4 @@ + +async function getUsers(names) { + /* your code */ +} diff --git a/5-network/01-fetch/01-fetch-users/_js.view/test.js b/5-network/01-fetch/01-fetch-users/_js.view/test.js new file mode 100644 index 0000000000..95eaf876e0 --- /dev/null +++ b/5-network/01-fetch/01-fetch-users/_js.view/test.js @@ -0,0 +1,10 @@ +describe("getUsers", function() { + + it("gets users from GitHub", async function() { + let users = await getUsers(['iliakan', 'remy', 'no.such.users']); + assert.equal(users[0].login, 'iliakan'); + assert.equal(users[1].login, 'remy'); + assert.equal(users[2], null); + }); + +}); diff --git a/5-network/01-fetch/01-fetch-users/solution.md b/5-network/01-fetch/01-fetch-users/solution.md new file mode 100644 index 0000000000..abb7d674cc --- /dev/null +++ b/5-network/01-fetch/01-fetch-users/solution.md @@ -0,0 +1,41 @@ + +ユーザをフェッチするには次が必要です: + +1. `fetch('https://api.github.com/users/USERNAME')`. +2. レスポンスのステータスが `200` であれば、JS オブジェクトを読むため `.json()` を呼び出します。 + +`fetch` が失敗、またはレスポンスステータスが200以外の数値の場合は、結果の配列で単に `null` を返します。 + +これはそのコードです: + +```js demo +async function getUsers(names) { + let jobs = []; + + for(let name of names) { + let job = fetch(`https://api.github.com/users/${name}`).then( + successResponse => { + if (successResponse.status != 200) { + return null; + } else { + return successResponse.json(); + } + }, + failResponse => { + return null; + } + ); + jobs.push(job); + } + + let results = await Promise.all(jobs); + + return results; +} +``` + +注意: `.then` 呼び出しは、`fetch` に直接アタッチされています。そのため、レスポンスがある場合には他のフェッチは待たずにすぐに `.json()` を読み始めます。 + +`await Promise.all(names.map(name => fetch()...)))` を使用して、その結果に対して `.json()` を呼び出すと、すべてのフェッチが応答するのを待ちます。 `.json()` を各 `fetch` に直接追加することで、個々のフェッチがお互いを待たずにデータをJSONとして読み始めることを保証します。 + +これは、たとえ私たちが主に `async/await` を使っていても、低レベルの `Promise` APIがいかに有用であるかの例です。 \ No newline at end of file diff --git a/5-network/01-fetch/01-fetch-users/task.md b/5-network/01-fetch/01-fetch-users/task.md new file mode 100644 index 0000000000..a096480202 --- /dev/null +++ b/5-network/01-fetch/01-fetch-users/task.md @@ -0,0 +1,12 @@ +# Github からユーザをフェッチする + +Github のログイン(ユーザ名)の配列を取得し、Github からユーザをフェッチし、Github ユーザの配列を返す非同期関数 `getUsers(names)` を作成してください。 + +指定された `USERNAME` に対するユーザ情報の Github url は `https://api.github.com/users/USERNAME` です。 + +サンドボックスにテスト例があります。 + +重要な点: + +1. ユーザ毎に1つの `fetch` リクエストがあるはずです。また、リクエストはお互い待つ必要はありません。データはなるべく早く取得できるようにしてください。 +2. リクエストが失敗した場合、またはそのようなユーザがいない場合は、関数は結果の配列で `null` を返します。 diff --git a/5-network/01-fetch/article.md b/5-network/01-fetch/article.md new file mode 100644 index 0000000000..ad7ef24765 --- /dev/null +++ b/5-network/01-fetch/article.md @@ -0,0 +1,303 @@ + +# Fetch + +JavaScript は、必要に応じていつでもサーバへリクエストを送信し、新しい情報を読み込むことができます。 + +例えば、次のようなことができます: + +- 注文を送信する +- ユーザ情報を読み込む +- サーバから最新の更新情報を受け取る +- ...など + +...そしてこれらはすべてページをリロードすることなく行うことができます。 + +それを表す包括的な用語 "AJAX" (<b>A</b>synchronous <b>J</b>avascript <b>A</b>nd <b>X</b>ml)があります。XML を使う必要はありません: この用語は昔から来ています。 + +ネットワークリクエストを送信し、サーバから情報を取得するための様々な方法があります。 + +`fetch()` メソッドはモダンで多目的に利用できるため、これから始めましょう。`fetch` は数年間進化と改善を続けています。今のところサポートはブラウザの間でとてもしっかりしています。 + +基本構文は次の通りです: + +```js +let promise = fetch(url, [options]) +``` + +- **`url`** -- アクセスする URL +- **`options`** -- オプションのパラメータ: メソッドやヘッダなど + +ブラウザはすぐにリクエストを開始し、`promise` を返します。 + +レスポンスの取得は通常2段階のプロセスになります。 + +**`promise` は、サーバがヘッダを応答するとすぐに組み込みの [Response](https://fetch.spec.whatwg.org/#response-class) クラスのオブジェクトで resolve します。** + +そのため、HTTP ステータスをチェックすることで、リクエストが成功したのか失敗したのかを確認したり、ヘッダをチェックすることができます。が、まだ本文は持っていません。 + +promise は `fetch` が HTTP リクエストを作るすることができなかった場合、例えば ネットワークの問題やそのようなサイトがない場合に reject します。404 や 500 のような HTTP エラーも通常のフローとみなされます。 + +レスポンスのプロパティでそれらを確認することができます。: + +- **`ok`** -- boolean, HTTP ステータスコードが 200-299 の場合 `true` です。 +- **`status`** -- HTTP ステータスコード. + +例: + +```js +let response = await fetch(url); + +if (response.ok) { // HTTP ステータスが 200-299 の場合 + // レスポンスの本文を取得(後述) + let json = await response.json(); +} else { + alert("HTTP-Error: " + response.status); +} +``` + +レスポンスの本文を取得するには、追加のメソッド呼び出しが必要です。 + +`Response` は様々な形式で本文にアクセスするための、複数の promise ベースのメソッドを提供しています。: + +- **`response.json()`** -- レスポンスを JSON オブジェクトとしてパースします +- **`response.text()`** -- レスポンスをテキストとして返します +- **`response.formData()`** -- FormData オブジェクト(form/multipart encoding) として返します, +- **`response.blob()`** -- [Blob](info:blob) (型付きのバイナリデータ) としてレスポンスを返します, +- **`response.arrayBuffer()`** -- [ArrayBuffer](info:arraybuffer-binary-arrays) (純粋なバイナリデータ) としてレスポンスを返します, +- 加えて, `response.body` は [ReadableStream](https://streams.spec.whatwg.org/#rs-class) オブジェクトで、本文をチャンク毎に読むことができます。後ほど例を見ていきましょう。 + +例えば、ここでは Github から最新のコミットの JSON オブジェクトを取得します。: + +```js run async +let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'); + +*!* +let commits = await response.json(); // レスポンスの本文を読み JSON としてパースする +*/!* + +alert(commits[0].author.login); +``` + +もしくは、純粋な promise 構文を使用した場合は次のようになります: + +```js run +fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits') + .then(response => response.json()) + .then(commits => alert(commits[0].author.login)); +``` + +テキストを取得するには: +```js +let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'); + +let text = await response.text(); // レスポンスボディをテキストとして読む + +alert(text.slice(0, 80) + '...'); +``` + +また、バイナリの例では、画像を取得して表示してみましょう(blob に対する操作の詳細については、チャプター [Blob](info:blob) を参照してください)。: + +```js async run +let response = await fetch('/article/fetch/logo-fetch.svg'); + +*!* +let blob = await response.blob(); // Blob オブジェクトとしてダウンロード +*/!* + +// <img> を作成 +let img = document.createElement('img'); +img.style = 'position:fixed;top:10px;left:10px;width:100px'; +document.body.append(img); + +// 表示 +img.src = URL.createObjectURL(blob); + +setTimeout(() => { // 3秒後に隠す + img.remove(); + URL.revokeObjectURL(img.src); +}, 3000); +``` + +````warn +本文のパース方法は1つだけ選ぶことができます。 + +もし `response.text()` でレスポンスを取得した後、`response.json()` は動作しません。本文のコンテンツは既に処理されているためです。 + +```js +let text = await response.text(); // 本文を返す +let parsed = await response.json(); // 失敗 (既に本文は処理済み) +```` + +## Headers + +`response.headers` には、Mapライクなヘッダオブジェクトがあります。 + +個々のヘッダを取得したり、それらをイテレートすることができます。 + +```js run async +let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'); + +// ヘッダを1つ取得 +alert(response.headers.get('Content-Type')); // application/json; charset=utf-8 + +// すべてのヘッダをイテレート +for (let [key, value] of response.headers) { + alert(`${key} = ${value}`); +} +``` + +ヘッダを設定するには、次のように `headers` オプションを使用します: + +```js +let response = fetch(protectedUrl, { + headers: { + Authentication: 'abcdef' + } +}); +``` + +...しかし、設定できない [禁止された HTTP ヘッダ](https://fetch.spec.whatwg.org/#forbidden-header-name) のリストがあります。: + +- `Accept-Charset`, `Accept-Encoding` +- `Access-Control-Request-Headers` +- `Access-Control-Request-Method` +- `Connection` +- `Content-Length` +- `Cookie`, `Cookie2` +- `Date` +- `DNT` +- `Expect` +- `Host` +- `Keep-Alive` +- `Origin` +- `Referer` +- `TE` +- `Trailer` +- `Transfer-Encoding` +- `Upgrade` +- `Via` +- `Proxy-*` +- `Sec-*` + +これらのヘッダは適切で安全な HTTP を保証するため、ブラウザによってのみ制御されます。 + +## POST リクエスト + +`POST`、もしくは他のメソッドを使ったリクエストを行うには、`fetch` のオプションを利用します: + +- **`method`** -- HTTP メソッド, e.g. `POST`, +- **`body`** -- 次のいずれか: + - 文字列 (e.g. JSON), + - `FormData` オブジェクト, `form/multipart` としてデータを送信する場合, + - `Blob`/`BufferSource` バイナリデータを送信する場合, + - [URLSearchParams](info:url), `x-www-form-urlencoded` としてデータを送信する場合。ほとんど使われません。 + +例を見てみましょう。このコードは、`user` オブジェクトを JSON として送信します。: + +```js run async +let user = { + name: 'John', + surname: 'Smith' +}; + +*!* +let response = await fetch('/article/fetch-basics/post/user', { + method: 'POST', + headers: { + 'Content-Type': 'application/json;charset=utf-8' + }, + body: JSON.stringify(user) +}); +*/!* + +let result = await response.json(); +alert(result.message); +``` + +本文(body)が文字列の場合、`Content-Type` にはデフォルトでは `text/plain` が設定されることに留意してください。そのため、`application/json` を代わりに送信するために `headers` オプションを使用しています。 + +## 画像を送信する + +`Blob` や `BufferSource` を使用して、バイナリデータを直接送信することもできます。 + +例えば、ここにマウスを動かすことで描画ができる `<canvas>` があります。"submit" ボタンをクリックすると、画像をサーバに送信します。: + +```html run autorun height="90" +<body style="margin:0"> + <canvas id="canvasElem" width="100" height="80" style="border:1px solid"></canvas> + + <input type="button" value="Submit" onclick="submit()"> + + <script> + canvasElem.onmousemove = function(e) { + let ctx = canvasElem.getContext('2d'); + ctx.lineTo(e.clientX, e.clientY); + ctx.stroke(); + }; + + async function submit() { + let blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png')); + let response = await fetch('/article/fetch-basics/post/image', { + method: 'POST', + body: blob + }); + let result = await response.json(); + alert(result.message); + } + + </script> +</body> +``` + +ここでも `Content-Type` を手動で設定する必要がありませんでした。なぜなら、`Blob` オブジェクトは組み込みのタイプ(`toBlob` により生成される `image/png`)を持っているからです。 + +`submit()` 関数はこのように `async/await` なしで書くこともできます。: + +```js +function submit() { + canvasElem.toBlob(function(blob) { + fetch('/article/fetch-basics/post/image', { + method: 'POST', + body: blob + }) + .then(response => response.json()) + .then(result => alert(JSON.stringify(result, null, 2))) + }, 'image/png'); +} +``` + + +## サマリ + +典型的な fetch リクエストは2つの `await` から成ります: + +```js +let response = await fetch(url, options); // レスポンスヘッダで resolve する +let result = await response.json(); // 本文を json として読む +``` + +もしくは、promise を使った書き方: +```js +fetch(url, options) + .then(response => response.json()) + .then(result => /* result を処理する */) +``` + +レスポンスのプロパティ: +- `response.status` -- レスポンスの HTTP コード, +- `response.ok` -- ステータスが 200-299 の場合 `true` +- `response.headers` -- HTTP ヘッダを持つ Mapライクなオブジェクト + +レスポンス本文を取得するメソッド: +- **`response.json()`** -- レスポンスを JSON オブジェクトとしてパースする, +- **`response.text()`** -- レスポンスをテキストとして返す, +- **`response.formData()`** -- FormData オブジェクト(form/multipart encoding) として返す, +- **`response.blob()`** -- [Blob](info:blob) (型付きのバイナリデータ) としてレスポンスを返す, +- **`response.arrayBuffer()`** -- [ArrayBuffer](info:arraybuffer-binary-arrays) (純粋なバイナリデータ) としてレスポンスを返す, + +これまでのところ、fetch オプションは次の通りです: +- `method` -- HTTP メソッド, +- `headers` -- リクエストヘッダを持つオブジェクト(どんなヘッダも許可されるわけではありません), +- `body` -- サブミットする string/FormData/BufferSource/Blob/UrlSearchParams データ。 + +次のチャプターでは、より多くのオプションとユースケースを見ていきます。 diff --git a/5-network/01-fetch/logo-fetch.svg b/5-network/01-fetch/logo-fetch.svg new file mode 100644 index 0000000000..5a58b209b5 --- /dev/null +++ b/5-network/01-fetch/logo-fetch.svg @@ -0,0 +1,4 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> +<circle cx="50" cy="50" r="45" fill="#fff" stroke="#3c790a" stroke-width="10"/> +<path d="m34,55a60,60,0,0,0,20,-20a6,10,0,0,1,13,-1a10,6,0,0,1,-1,13a60,60,0,0,0,-20,20a6,10,0,0,1,-13,1a10,6,0,0,1,1,-13" fill="#3c790a"/> +</svg> diff --git a/5-network/01-fetch/post.view/server.js b/5-network/01-fetch/post.view/server.js new file mode 100644 index 0000000000..18610676a0 --- /dev/null +++ b/5-network/01-fetch/post.view/server.js @@ -0,0 +1,57 @@ +const Koa = require('koa'); +const app = new Koa(); +const bodyParser = require('koa-bodyparser'); +const getRawBody = require('raw-body') +const busboy = require('async-busboy'); +const Router = require('koa-router'); + +let router = new Router(); + +router.post('/user', async (ctx) => { + ctx.body = { + message: "User saved." + }; +}); + +router.post('/image', async (ctx) => { + let body = await getRawBody(ctx.req, { + limit: '1mb' + }); + ctx.body = { + message: `Image saved, size:${body.length}.` + }; +}); + +router.post('/image-form', async (ctx) => { + + let files = []; + const { fields } = await busboy(ctx.req, { + onFile(fieldname, file, filename, encoding, mimetype) { + // read all file stream to continue + getRawBody(file, { limit: '1mb'}).then(body => { + files.push({ + fieldname, + filename, + length: body.length + }); + }) + + } + }); + + ctx.body = { + message: `Image saved, name: ${fields.name}, size:${files[0].length}.` + }; +}); + +app + .use(bodyParser()) + .use(router.routes()) + .use(router.allowedMethods()); + + +if (!module.parent) { + http.createServer(app.callback()).listen(8080); +} else { + exports.accept = app.callback(); +} diff --git a/5-network/02-formdata/article.md b/5-network/02-formdata/article.md new file mode 100644 index 0000000000..a45bc23de0 --- /dev/null +++ b/5-network/02-formdata/article.md @@ -0,0 +1,178 @@ +# FormData + +このチャプターは HTML フォームの送信について説明します: ファイル有無や追加のフィールドなど。[FormData](https://xhr.spec.whatwg.org/#interface-formdata) オブジェクトはそれらの場合に役立ちます。 + +コンストラクタは次の通りです: +```js +let formData = new FormData([form]); +``` + +HTML `form` 要素が提供されている場合、自動的にそのフィールドを捉えます。ご想像のとおり、 `FormData` はフォームデータを保存したり送信したりするためのオブジェクトです。 + +`FormData` に関して特別なことは、`fetch` のようなネットワークメソッドが、本体(ボディ)として `FormData` オブジェクトを受け入れることができるという点です。エンコードされ、`Content-Type: form/multipart` で送信されます。なので、サーバ側から見ると、通常のフォーム送信のように見えます。 + +## シンプルなフォームの送信 + +最初に、シンプルなフォームを送信してみましょう。 + +ご覧の通り、ほぼ1行でです。 + +```html run +<form id="formElem"> + <input type="text" name="name" value="John"> + <input type="text" name="surname" value="Smith"> +</form> + +<script> +(async () => { + let response = await fetch('/article/fetch-basics/post/user', { + method: 'POST', +*!* + body: new FormData(formElem) +*/!* + }); + + let result = await response.json(); + + alert(result.message); +})(); +</script> +``` + +ここでは、サーバーはフォームを使用したPOST要求を受け入れ、 "User saved" と返信します。 + +## FormData メソッド + +次のメソッドを使って `FormData` のフィールドを変更することができます: + +- `formData.append(name, value)` - 指定された `name` と `value` のフォームフィールドを追加します。 +- `formData.append(name, blob, fileName)` - `<input type="file">` のようにフィールドを追加します。3つ目の引数 `fileName` はファイル名を設定します(フィールド名ではありません)。ファイルシステムでのファイル名です。 +- `formData.delete(name)` - 指定された `name` のフィールドを削除します。 +- `formData.get(name)` - 指定された `name` のフィールド値を取得します。 +- `formData.has(name)` - 指定された `name` のフィールドが存在する場合には `true` を、そうでなければ `false` を返します。 + +フォームは、技術的には同じ `name` をもつ複数のフィールドを持つことが可能なので、複数の `append` 呼び出しをすると、その分同じ名前のフィールドが追加されます。 + +`append` と同じ構文の `set` メソッドもあります。違いは `.set` は指定された `name` のフィールドをすべて削除し、その後新しいフィールドを追加します。なので、`set` をした場合、`name` のフィールドが1つであることを確認してみてください。: + +- `formData.set(name, value)`, +- `formData.set(name, blob, fileName)`. + +また、`for..of` ループを使用して、formData フィールドを反復することもできます: + +```js run +let formData = new FormData(); +formData.append('key1', 'value1'); +formData.append('key2', 'value2'); + +// key/value ペアをリストします +for(let [name, value] of formData) { + alert(`${name} = ${value}`); // key1=value1, then key2=value2 +} +``` + +## ファイルを含むフォームを送信する + +フォームは常に `Content-Type: form/multipart` として送信され、このエンコーディングはファイルを送信することが可能です。そのため、通常のフォーム送信と同様に、 `<input type="file">` フィールドも送信できます。 + +これは、そのようなフォームの例です: + +```html run autorun +<form id="formElem"> + <input type="text" name="firstName" value="John"> + Picture: <input type="file" name="picture" accept="image/*"> + <input type="submit"> +</form> + +<script> + formElem.onsubmit = async (e) => { + e.preventDefault(); + + let response = await fetch('/article/formdata/post/user-avatar', { + method: 'POST', +*!* + body: new FormData(formElem) +*/!* + }); + + let result = await response.json(); + + alert(result.message); + }; +</script> +``` + +## Blob データを含むフォームを送信する + +チャプター <info:fetch> で見てきたように、動的に生成された `Blob` を送信します(e.g 画像)。`fetch` パラメータの `body` に直接指定することができます。 + +しかし、実際には画像を個別に送信するのではなく、フォームの一部として、"name" や他のメタデータのような追加のフィールドと一緒に送信するのが便利なことが多いです。 + +また、通常サーバは生のバリナリデータよりもマルチパートエンコード形式を受け入れるのに適しています。 + +この例は、`FormData` を使って他のフィールドと一緒に `<canvas>` からの画像を送信します。: + +```html run autorun height="90" +<body style="margin:0"> + <canvas id="canvasElem" width="100" height="80" style="border:1px solid"></canvas> + + <input type="button" value="Submit" onclick="submit()"> + + <script> + canvasElem.onmousemove = function(e) { + let ctx = canvasElem.getContext('2d'); + ctx.lineTo(e.clientX, e.clientY); + ctx.stroke(); + }; + + async function submit() { + let imageBlob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png')); + +*!* + let formData = new FormData(); + formData.append("firstName", "John"); + formData.append("image", imageBlob, "image.png"); +*/!* + + let response = await fetch('/article/formdata/post/image-form', { + method: 'POST', + body: formData + }); + let result = await response.json(); + alert(result.message); + } + + </script> +</body> +``` + +画像 `Blob` がどのように追加されているかに注目してください: + +```js +formData.append("image", imageBlob, "image.png"); +``` + +これは、フォームに `<input type="file" name="image">` があり、訪問者がファイルシステムから `image.png` (3番目の引数) という名前のファイルを送信したのと同じです。 + +## サマリ + +[FormData](https://xhr.spec.whatwg.org/#interface-formdata) オブジェクトは HTML フォームをキャプチャして(捉えて)、それらを `fetch` や他のネットワークリクエストを使用して送信するために使われます。 + +HTML フォームから `new FormData(form)` 作成する、あるいは空のオブジェクト作成してから次のメソッドでフィールドを追加することができます。: + +- `formData.append(name, value)` +- `formData.append(name, blob, fileName)` +- `formData.set(name, value)` +- `formData.set(name, blob, fileName)` + +2つの特徴があります: +1. `set` メソッドは同じ名前のフィールドを削除します。`append` は削除しません。 +2. ファイルを送信するには、3つの引数の構文が必要になります。最後の引数はファイル名であり、`<input type="file">` の場合、通常はユーザのファイルシステムからとられます。 + +他のメソッドは次のとおりです: + +- `formData.delete(name)` +- `formData.get(name)` +- `formData.has(name)` + +以上です! diff --git a/5-network/02-formdata/post.view/server.js b/5-network/02-formdata/post.view/server.js new file mode 100644 index 0000000000..51605dbf39 --- /dev/null +++ b/5-network/02-formdata/post.view/server.js @@ -0,0 +1,78 @@ +const Koa = require('koa'); +const app = new Koa(); +const bodyParser = require('koa-bodyparser'); +const getRawBody = require('raw-body') +const busboy = require('async-busboy'); +const Router = require('koa-router'); + +let router = new Router(); + +router.post('/user', async (ctx) => { + ctx.body = { + message: "User saved." + }; +}); + +router.post('/image-form', async (ctx) => { + + let files = []; + const { fields } = await busboy(ctx.req, { + onFile(fieldname, file, filename, encoding, mimetype) { + // read all file stream to continue + let length = 0; + file.on('data', function(data) { + length += data.length; + }); + file.on('end', () => { + files.push({ + fieldname, + filename, + length + }); + }); + } + }); + + ctx.body = { + message: `Image saved, firstName: ${fields.firstName}, Image size:${files[0].length}, fileName: ${files[0].filename}.` + }; +}); + + +router.post('/user-avatar', async (ctx) => { + + let files = []; + const { fields } = await busboy(ctx.req, { + onFile(fieldname, file, filename, encoding, mimetype) { + // read all file stream to continue + let length = 0; + file.on('data', function(data) { + length += data.length; + }); + file.on('end', () => { + files.push({ + fieldname, + filename, + length + }); + }); + + } + }); + + ctx.body = { + message: `User with picture, firstName: ${fields.firstName}, picture size:${files[0].length}.` + }; +}); + +app + .use(bodyParser()) + .use(router.routes()) + .use(router.allowedMethods()); + + +if (!module.parent) { + http.createServer(app.callback()).listen(8080); +} else { + exports.accept = app.callback(); +} diff --git a/5-network/03-fetch-progress/article.md b/5-network/03-fetch-progress/article.md new file mode 100644 index 0000000000..4bfb29f021 --- /dev/null +++ b/5-network/03-fetch-progress/article.md @@ -0,0 +1,104 @@ + +# Fetch: ダウンロードの進行状況 + +fetch はダウンロードの進行状況を追跡することができます。が、アップロードの進行状況の追跡はできません。 + +注意してください: 現在アップロードの進行状況を取得する方法はありません。それをするには、[XMLHttpRequest](info:xmlhttprequest) を使用してください。 + +ダウンロードの進行状況を追跡するには、`response.body` プロパティを使用します。これは "読み取り可能なストリーム" です。これはチャンクごとに本文を提供する特別なオブジェクトなので、その時点でどのくらい利用できるのかを確認することができます。 + +これは、レスポンスを読み込むのにそれを使ったコードの概略を説明したものです。: + +```js +// response.json() などの他のメソッドの代わり +const reader = response.body.getReader(); + +// 本文のダウンロードは無限ループ +while(true) { + // 最後のチャンクも場合、done は true。 + // value はチャンクバイトの Uint8Array + const {done, value} = await reader.read(); + + if (done) { + break; + } + + console.log(`Received ${value.length} bytes`) +} +``` + +`await reader.read()` がレスポンスのチャンクを返す間、ループします。 + +チャンクは2つのプロパティを持っています。: +- **`done`** -- 読み込みが完了すると true になります。 +- **`value`** -- 型付きのバイト配列: `Uint8Array`. + +進行状況を記録するには、チャンクを数える必要があります。 + +これはレスポンスを取得し、進行状況を記録する完全なコードです。: + +```js run async +// Step 1: fetch を開始し、reader を取得します +let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits?per_page=100'); + +const reader = response.body.getReader(); + +// Step 2: 合計の長さを取得します +const contentLength = +response.headers.get('Content-Length'); + +// Step 3: データを読み込みます +let receivedLength = 0; // その時点の長さ +let chunks = []; // 受信したバイナリチャンクの配列(本文を構成します) +while(true) { + const {done, value} = await reader.read(); + + if (done) { + break; + } + + chunks.push(value); + receivedLength += value.length; + + console.log(`Received ${receivedLength} of ${contentLength}`) +} + +// Step 4: チャンクを1つの Uint8Array に連結します +let chunksAll = new Uint8Array(receivedLength); // (4.1) +let position = 0; +for(let chunk of chunks) { + chunksAll.set(chunk, position); // (4.2) + position += chunk.length; +} + +// Step 5: 文字列にデコードします +let result = new TextDecoder("utf-8").decode(chunksAll); + +// 完了! +let commits = JSON.parse(result); +alert(commits[0].author.login); +``` + +ステップ毎の説明です: + +1. 通常通り `fetch` を実行しますが、 `response.json()`, の代わりに `response.body.getReader()` を取得します。 + + 注意してください。同一のレスポンスを読み込むのに、これら両方のメソッドを使用することはできません。結果を得るには、reader を使用するか、レスポンスメソッドを使用するかのいずれかです。 +2. 読み込む前に、`Content-Length` ヘッダからレスポンス全体の長さを知ることができます。 + + クロスドメインリクエスト(チャプター <info:fetch-crossorigin> を参照)の場合、ヘッダはない場合があります。同様に、技術的にはサーバは設定する必要はありませんが、通常はそこにあります。 +3. 完了(done)するまで、 `await reader.read()` を呼び出します。 + + ここでは配列にレスポンスの `chunks` を集めていますが、これは重要です。なぜなら、レスポンス消費後は、`response.json()` や別の方法を使って "再読み取り" することはできないからです(やるとエラーになります)。 +4. 最後に、`chunks` -- `Uint8Array` バイトチャンクの配列を1つの結果に結合する必要があります。残念ながら、それらを連結する単一のメソッドはないので、いくらかコードを書く必要があります。: + 1. `new Uint8Array(receivedLength)` -- 連結された長さをもつ、同じ型の配列を作成します。 + 2. 次に `.set(chunk, position)` メソッドで結果となる配列に各 `chunk` を続々とコピーします。 +5. `chunksAll` に結果がありますが、これは文字列ではなくバイト配列です。 + + 文字列を作るには、これらのバイトを解釈する必要があります。組み込みの [TextDecoder](info:text-decoder) はまさにそれを行います。その後、`JSON.parse` することができます。 + +仮に JSON の代わりにバイナリのコンテンツが必要な場合はどうなるでしょう?その場合はよりシンプルです。Step 4, 5 の代わりに、すべてのチャンクの blob を作ります。 +```js +let blob = new Blob(chunks); +``` + +繰り返しますが、これはアップロードの進行状況ではなく(今のところ方法はありません)、ダウンロードの進行状況についてのみであることに留意してください。 diff --git a/5-network/03-fetch-progress/logo-fetch.svg b/5-network/03-fetch-progress/logo-fetch.svg new file mode 100644 index 0000000000..5a58b209b5 --- /dev/null +++ b/5-network/03-fetch-progress/logo-fetch.svg @@ -0,0 +1,4 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> +<circle cx="50" cy="50" r="45" fill="#fff" stroke="#3c790a" stroke-width="10"/> +<path d="m34,55a60,60,0,0,0,20,-20a6,10,0,0,1,13,-1a10,6,0,0,1,-1,13a60,60,0,0,0,-20,20a6,10,0,0,1,-13,1a10,6,0,0,1,1,-13" fill="#3c790a"/> +</svg> diff --git a/5-network/03-fetch-progress/progress.view/index.html b/5-network/03-fetch-progress/progress.view/index.html new file mode 100644 index 0000000000..ba7f76065e --- /dev/null +++ b/5-network/03-fetch-progress/progress.view/index.html @@ -0,0 +1,36 @@ +<!doctype html> +<script> +(async () { + + const response = await fetch('long.txt'); + const reader = response.body.getReader(); + + const contentLength = +response.headers.get('Content-Length'); + let receivedLength = 0; + let chunks = []; + while(true) { + const chunk = await reader.read(); + + if (chunk.done) { + console.log("done!"); + break; + } + + chunks.push(chunk.value); + receivedLength += chunk.value.length; + console.log(`${receivedLength}/${contentLength} received`) + } + + + let chunksMerged = new Uint8Array(receivedLength); + let length = 0; + for(let chunk of chunks) { + chunksMerged.set(chunk, length); + length += chunk.length; + } + + let result = new TextDecoder("utf-8").decode(chunksMerged); + console.log(result); +})(); + +</script> diff --git a/5-network/03-fetch-progress/progress.view/long.txt b/5-network/03-fetch-progress/progress.view/long.txt new file mode 100644 index 0000000000..a98e50a280 --- /dev/null +++ b/5-network/03-fetch-progress/progress.view/long.txt @@ -0,0 +1,20465 @@ +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. +A long file. Длинный файл. 长文件. diff --git a/5-network/04-fetch-abort/article.md b/5-network/04-fetch-abort/article.md new file mode 100644 index 0000000000..985dfe6f78 --- /dev/null +++ b/5-network/04-fetch-abort/article.md @@ -0,0 +1,111 @@ + +# Fetch: Abort + +`fetch` を中止するのは少し面倒です。思い出してください、`fetch` は promise を返します。そして、JavaScript には一般的に promise を "中止する" という概念はありません。では、どうやって fetch をキャンセルしましょう? + +このような目的のための、特別な組み込みのオブジェクトがあります。: +`AbortController`. + +使い方はとても簡単です: + +- Step 1: コントローラを作成します: + + ```js + let controller = new AbortController(); + ``` + + コントローラは非常にシンプルなオブジェクトです。単一のメソッド `abort()` と、単一のプロパティ `signal` を持っています。`abort()` が呼ばれると、`abort` イベントが `controller.signal` で発生します。: + + このようになります: + + ```js run + let controller = new AbortController(); + let signal = controller.signal; + + // controller.abort() が呼ばれるとトリガーします + signal.addEventListener('abort', () => alert("abort!")); + + controller.abort(); // abort! + + alert(signal.aborted); // true (abort 後) + ``` + +- Step 2: `signal` プロパティを `fetch` オプションに渡します: + + ```js + let controller = new AbortController(); + fetch(url, { + signal: controller.signal + }); + ``` + + これで `fetch` は signal をリッスンします。 + +- Step 3: 中止するために `controller.abort()` を呼びます: + + ```js + controller.abort(); + ``` + + これで終わりです: `fetch` は `signal` からのイベントを得て、リクエストを中止します。 + +fetch が中止されたとき、その promise は `AbortError` という名前のエラーで reject されます。なので、次のように処理できます: + +```js run async +// 1秒で中止 +let controller = new AbortController(); +setTimeout(() => controller.abort(), 1000); + +try { + let response = await fetch('/article/fetch-abort/demo/hang', { + signal: controller.signal + }); +} catch(err) { + if (err.name == 'AbortError') { // abort() を処理 + alert("Aborted!"); + } else { + throw err; + } +} +``` + +**`AbortController` はスケーラブルで, 複数の fetch を一度にキャンセルすることができます。** + +例えばここでは、平行して複数の `urls` を fetch し、コントローラはそれらすべてを中止します。: + +```js +let urls = [...]; // 平行して fetch する url のリスト + +let controller = new AbortController(); + +let fetchJobs = urls.map(url => fetch(url, { + signal: controller.signal +})); + +let results = await Promise.all(fetchJobs); + +// 他の場所から: +// controller.abort() ですべての fetch を停止します +``` + +もし `fetch` とは別の独自のジョブがある場合も、一つの `AbortController` を使用して fetch と一緒にそれらを停止することができます。 + + +```js +let urls = [...]; +let controller = new AbortController(); + +let ourJob = new Promise((resolve, reject) => { + ... + controller.signal.addEventListener('abort', reject); +}); + +let fetchJobs = urls.map(url => fetch(url, { + signal: controller.signal +})); + +let results = await Promise.all([...fetchJobs, ourJob]); + +// 他の場所から: +// controller.abort() ですべての fetch と独自のジョブを停止します +``` diff --git a/5-network/04-fetch-abort/demo.view/server.js b/5-network/04-fetch-abort/demo.view/server.js new file mode 100644 index 0000000000..5a0c8e4a54 --- /dev/null +++ b/5-network/04-fetch-abort/demo.view/server.js @@ -0,0 +1,21 @@ +const Koa = require('koa'); +const app = new Koa(); + +const Router = require('koa-router'); + +let router = new Router(); + +router.get('/hang', async (ctx) => { + await new Promise(() => {}); +}); + +app + .use(router.routes()) + .use(router.allowedMethods()); + + +if (!module.parent) { + http.createServer(app.callback()).listen(8080); +} else { + exports.accept = app.callback(); +} diff --git a/5-network/05-fetch-crossorigin/1-do-we-need-origin/solution.md b/5-network/05-fetch-crossorigin/1-do-we-need-origin/solution.md new file mode 100644 index 0000000000..8f23097d6c --- /dev/null +++ b/5-network/05-fetch-crossorigin/1-do-we-need-origin/solution.md @@ -0,0 +1,9 @@ +`Origin` は必要です。なぜなら、`Referer` はない場合があるからです。例えば、HTTPS で HTTP のページを `fetch` するとき(よりセキュアな場所からセキュアでない場所へアクセスするとき)、`Referer` はありません。 + +[コンテンツセキュリティポリシー(Content Security Policy: CSP)](http://en.wikipedia.org/wiki/Content_Security_Policy) は `Referer` を送信するのを禁止する可能性があります。 + +後ほど分かりますが、`fetch` にも `Referer` の送信を防いだり、それを変更することを許可する(同じサイト内で)オプションがあります。 + +仕様によると、`Referer` はオプションの HTTP ヘッダです。 + +`Referer` は信頼できないため、`Origin` が発明されました。ブラウザはクロスオリジンリクエストのために、正しい `Origin` を保証します。 diff --git a/5-network/05-fetch-crossorigin/1-do-we-need-origin/task.md b/5-network/05-fetch-crossorigin/1-do-we-need-origin/task.md new file mode 100644 index 0000000000..efb416dd40 --- /dev/null +++ b/5-network/05-fetch-crossorigin/1-do-we-need-origin/task.md @@ -0,0 +1,28 @@ +importance: 5 + +--- + +# なぜ Origin が必要なのでしょう? + +恐らくご存知の通り、HTTP ヘッダには `Referer` があります。これは通常、ネットワークリクエストを開始したページの URL が含まれています。 + +例えば、`http://javascript.info/some/url` から `http://google.com` を取得しようとすると、そのヘッダは次のようになります。: + +``` +Accept: */* +Accept-Charset: utf-8 +Accept-Encoding: gzip,deflate,sdch +Connection: keep-alive +Host: google.com +*!* +Origin: http://javascript.info +Referer: http://javascript.info/some/url +*/!* +``` + +ご覧の通り、`Referer` と `Origin` 両方が存在します。 + +ここで質問です: + +1. `Referer` がより多くの情報を持っているのに、なぜ ` Origin` が必要なのでしょうか。 +2. `Referer` や `Origin` がない、あるいはそれが正しくない可能性はありますか? diff --git a/5-network/05-fetch-crossorigin/article.md b/5-network/05-fetch-crossorigin/article.md new file mode 100644 index 0000000000..7586980c31 --- /dev/null +++ b/5-network/05-fetch-crossorigin/article.md @@ -0,0 +1,368 @@ +# Fetch: クロスオリジン(Cross-Origin) リクエスト + +もし任意の web サイトから `fetch` を行った場合、そのリクエストは恐らく失敗するでしょう。 + +ここで中心となる概念は *オリジン* -- ドメイン/ポート/プロトコルの3つ揃いです。 + +クロスオリジンリクエスト(これらは別のドメイン(サブドメインも)、プロトコル、あるいはポートに送信されたもの)には、リモート側からの特別なヘッダが必要です。そのポリシーは "CORS" (Cross-Origin Resource Sharing) と呼ばれています。 + +例えば、`http://example.com` へのフェッチをしてみましょう。: + +```js run async +try { + await fetch('http://example.com'); +} catch(err) { + alert(err); // Failed to fetch +} +``` + +予想通り、fetch は失敗します。 + +## なぜ? + +なぜなら、クロスオリジン制約が悪意のあるハッカーからインターネットを保護するからです。 + +脱線しますが、簡単に歴史的な背景を振り返りましょう。 + +長い間、JavaScript はネットワークリクエストを実行するための特別なメソッドを持っていませんでした。 + +**あるサイトのスクリプトが別のサイトのコンテンツへアクセスすることはできませんでした。** + +このシンプルだけど強力なルールはインターネットセキュリティの基盤でした。例えば、ページ `hacker.com` のスクリプトは `gmail.com` にあるユーザのメールボックにはアクセスできませんでした。ユーザはそれを安全と思いました。 + +しかし、web 開発者はより強力な力を求めました。そしてそれを回避するための様々なトリックが考案されました。 + +別のサーバとやり取りする方法の1つは、そこに `<form>` を送信することでした。次のように、現在のページに留まるために `<iframe>` に送信しました。: + +```html +<!-- form target --> +<iframe name="iframe"></iframe> + +<!-- JavaScript により form は動的に生成され、サブミットされました --> +<form target="iframe" method="POST" action="http://another.com/…"> + ... +</form> + +``` + +- これにより、ネットワーキングのメソッドがなくても、別のサイトへ GET/POST リクエストを送ることは可能でした。 +- しかし、別のサイトから `<iframe>` のコンテンツにアクセスすることは禁止されているので、レスポンスを読むことはできませんでした。 + +したがって、`<form>` はどこにでもデータをサブミットすることを可能にしましたが、レスポンスのコンテンツにはアクセスできませんでした。 + +別の方法は `<script src="http://another.com/…">` タグを使用することでした。スクリプトは任意のドメインの `src` を持つことができます。しかし、改めて -- このようなスクリプトの生のコンテンツにアクセスすることは不可能でした。 + +もし `another.com` がこのような種類のアクセスに対してデータを公開するつもりだった場合、いわゆる "JSONP (JSPN with padding)" プロトコルが使われました。 + +これはそのフローです: + +1. まず、事前に e.g. `gotWeather` のような、データを受け取るためのグローバル関数を宣言します。 +2. 次に `<script>` を作り、その名前を `callback` クエリーパラメータとして渡します。e.g. `src="http://another.com/weather.json?callback=gotWeather"`. +3. リモートサーバは、データを `gotWeather(...)` 呼び出しにラップするようなレスポンスを動的に生成します。 +4. スクリプトが実行されると、`gotWeather` が実行されデータが得られます。 + +これは JSONP でデータを受け取るコードの例です: + +```js run +// 1. データを処理する関数を宣言します +function gotWeather({ temperature, humidity }) { + alert(`temperature: ${temperature}, humidity: ${humidity}`); +} + +// 2. スクリプトに対して、?callback パラメータとしてその名前を渡します +let script = document.createElement('script'); +script.src = `https://cors.javascript.info/article/fetch-crossorigin/demo/script?callback=gotWeather`; +document.body.append(script); + +// 3. サーバから期待する応答は次のようになります: +/* +gotWeather({ + temperature: 25, + humidity: 78 +}); +*/ +``` + +これは動作し、セキュリティにも違反しません。なぜなら、双方がこの方法でデータを渡すことに合意しているからです。双方が合意するときはハックではありません。これは非常に古いブラウザでも動作するため、このようなアクセスを提供するサービスはまだ存在します。 + +しばらくすると、現在のネットワークメソッドが登場しました。当初はクロスオリジンリクエストは禁止されていました。しかし、長い議論の結果、サーバによって明示的に許可されている場合に限り、クロスオリジンリクエストは許可されました。 + +## 単純リクエスト(Simple requests) + +[Simple requests](http://www.w3.org/TR/cors/#terminology) は次の条件を満たす必要があります。: + +1. [Simple method](http://www.w3.org/TR/cors/#simple-method): GET, POST または HEAD +2. [Simple headers](http://www.w3.org/TR/cors/#simple-header) -- 許可されているものだけ(以下): + - `Accept`, + - `Accept-Language`, + - `Content-Language`, + - 値が `application/x-www-form-urlencoded`, `multipart/form-data` あるいは `text/plain` の `Content-Type`. + +その他のリクエストは "単純ではない(non-simple)" とみなされます。例えば、`PUT` メソッドや、 `API-Key` HTTP ヘッダを持つリクエストはこれに限りません。 + +**本質的な違いは、"単純リクエスト" は、特別な方法を使うことなく `<form>` または `<script>` を使って作成することができることです。** + +そのため、たとえ非常に古いサーバでも、単純リクエストを受け入れる準備ができているはずです。 + +逆に、非標準のヘッダ、あるいは例えば `DELETE` メソッドのリクエストはこの方法では作ることはできません。長い間、JavaScript はこのようなリクエストを行うことができませんでした。なので、古いサーバは、そのようなリクエストは特権のある送信元から来たものであると想定する場合があります(web ページではそのようなリクエストは送信できないため)。 + +単純でないリクエストをしようとすろと、ブラウザは特別な "preflight" リクエストを送信します。これは、サーバにこのようなクロスオリジンリクエストを受け入れることに同意するか否かを尋ねるものです。 + +そして、サーバがヘッダで明示的にそれを確認しない限り、単純でないリクエストは送信されません。 + +それでは詳細に進みましょう。これらはすべて1つの目的のためにあります。それは、新しいクロスオリジンの機能がサーバからの明示的な許可がある場合にのみアクセス可能であることを保証するためです。 + +## 単純リクエストに対する CORS + +リクエストがクロスオリジンである場合、ブラウザは常に `Origin` ヘッダを追加します。 + +例えば、`https://javascript.info/page` から `https://anywhere.com/request` にリクエストを行う場合、ヘッダは次のようになるでしょう: + +``` +GET /request +Host: anywhere.com +*!* +Origin: https://javascript.info +*/!* +... +``` + +ご覧の通り、`Origin` はパスなしの正確なオリジン(ドメイン/プロトコル/ポート)を含みます。 + +サーバは `Origin` を検査することができ、このようなリクエストを受け入れることに同意すると、レスポンスに `Access-Control-Allow-Origin` という特別なヘッダを追加します。このヘッダは許可されたオリジン(このケースでは `https://javascript.info`)、あるいはアスタリスク `*` を含む必要があります。そして応答は成功します。そうでなければエラーになります。 + +ブラウザはここでは信頼された仲介者の役割を果たします。: +1. 正しい `Origin` がクロスドメインリクエストと一緒に送信されることを保証します。 +2. レスポンスの中で正しい `Access-Control-Allow-Origin` を確認すると、次は JavaScript アクセス、そうでなければエラーと共に禁止します。 + +![](xhr-another-domain.svg) + +これは "受け入れに同意した" 応答の例です: +``` +200 OK +Content-Type:text/html; charset=UTF-8 +*!* +Access-Control-Allow-Origin: https://javascript.info +*/!* +``` + +## レスポンスヘッダ + +クロスオリジンリクエストの場合、デフォルトでは JavaScript は "単純レスポンスヘッダ" にしかアクセスできません。: + +- `Cache-Control` +- `Content-Language` +- `Content-Type` +- `Expires` +- `Last-Modified` +- `Pragma` + +他のレスポンスヘッダは禁止されています。 + +```smart header="注意してください: `Content-Length` はありません" +注意: 上のリストに `Content-Length` ヘッダはありません! + +そのため、何かをダウンロードしていて進捗具合を追跡したい場合は、ヘッダにアクセスするために追加の許可が必要になります(下記参照)。 +``` + +JavaScript が他のレスポンスヘッダへアクセスするのを許可するには、サーバは `Access-Control-Expose-Headers` ヘッダに、アクセスを許可するヘッダをリストする必要があります。 + +例: + +``` +200 OK +Content-Type:text/html; charset=UTF-8 +Content-Length: 12345 +API-Key: 2c9de507f2c54aa1 +Access-Control-Allow-Origin: https://javascript.info +*!* +Access-Control-Expose-Headers: Content-Length,API-Key +*/!* +``` + +このような `Access-Control-Expose-Headers` ヘッダを使うことで、スクリプトが `Content-Length` と `API-Key` ヘッダにアクセスすることを許可します。 + + +## "単純ではない" リクエスト + +私たちは単なる `GET/POST` だけでなく、`PATCH` や `DELETE` などの HTTP メソッドを使うことができます。 + +以前は web ページがこのようなリクエストを行うことができるとは、誰も想定していませんでした。そのため、標準ではないメソッドを "ブラウザではない" という合図として扱う web サービスが存在する可能性があります。They can take it into account when checking access rights. + +そのため、解釈違いを避けるために、昔は扱えなかった "単純ではない" リクエストについては、ブラウザはすぐにはこのようなリクエストを行いません。その前に、許可を求めるための準備的な、いわゆる "preflight" リクエストを送信します。 + +preflight リクエストは `OPTIONS` メソッドを使い、本文はありません。 +- `Access-Control-Request-Method` ヘッダは要求されたメソッドを持ちます。 +- `Access-Control-Request-Headers` ヘッダは単純でない HTTP ヘッダのカンマ区切りのリストを提供します。 + +サーバがリクエストを処理するのに同意する場合、ステータス 200 で本文なしの応答を返します。 + +- レスポンスヘッダ `Access-Control-Allow-Methods` は許可されたメソッドを持たなければなりません。 +- レスポンスヘッダ `Access-Control-Allow-Headers` は許可されたヘッダのリストを持たなければなりません。 +- 加えて、ヘッダ `Access-Control-Max-Age` でパーミッションキャッシュする秒数を指定する場合があります。その場合、ブラウザは与えれたパーミッションを満たす後続のリクエストに対して preflight を送信する必要はありません。 + +![](xhr-preflight.svg) + +クロスドメインの `PATCH` リクエストの例でステップ毎にどのように動作するのか見ていきましょう(このメソッドはデータを更新するのによく使われます)。: + +```js +let response = await fetch('https://site.com/service.json', { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json' + 'API-Key': 'secret' + } +}); +``` + +リクエストが単純でない理由が3つあります: +- メソッドが `PATCH` +- `Content-Type` は次のいずれでもない: `application/x-www-form-urlencoded`, `multipart/form-data`, `text/plain`. +- カスタムの `API-Key` ヘッダ. + +### Step 1 (preflight リクエスト) + +ブラウザは自身で次のような preflight リクエストを投げます: + +``` +OPTIONS /service.json +Host: site.com +Origin: https://javascript.info +Access-Control-Request-Method: PATCH +Access-Control-Request-Headers: Content-Type,API-Key +``` + +- メソッド: `OPTIONS`. +- パス -- メインのリクエストと正確に同じ: `/service.json`. +- クロスオリジンの特別なヘッダ: + - `Origin` -- 送信元のオリジン. + - `Access-Control-Request-Method` -- リクエストされたメソッド + - `Access-Control-Request-Headers` -- カンマ区切りの "単純ではない" ヘッダのリスト + +### Step 2 (preflight レスポンス) + +サーバはステータス 200 と 次のヘッダを応答する必要があります: +- `Access-Control-Allow-Methods: PATCH` +- `Access-Control-Allow-Headers: Content-Type,API-Key`. + +これは今後のやり取りを許可します。そうでなければ、エラーが起きるでしょう。 + +サーバが他のメソッドとヘッダも待ち受けている場合、一度にそれらすべてをリストするのは理にかなっています。例: + +``` +200 OK +Access-Control-Allow-Methods: PUT,PATCH,DELETE +Access-Control-Allow-Headers: API-Key,Content-Type,If-Modified-Since,Cache-Control +Access-Control-Max-Age: 86400 +``` + +これでブラウザは `PATCH` が許可されたメソッドのリストにあり、両方のヘッダもリストにあるこが確認できたので、メインのリクエストを送信します。 + +加えて、preflight レスポンスは `Access-Control-Max-Age` ヘッダで指定された時間(86400 秒, 1日)キャッシュされます。そのため後続のリクエストでは preflight は起きません。それらはキャッシュの内容から許容と想定して、直接送信されます。 + +### Step 3 (実際のリクエスト) + +preflight が成功すると、ブラウザは本当のリクエストを行います。ここでのフローは単純リクエストの場合と同じです。 + +実際のリクエストは `Origin` ヘッダを持ちます(クロスオリジンのため)。: + +``` +PATCH /service.json +Host: site.com +Content-Type: application/json +API-Key: secret +Origin: https://javascript.info +``` + +### Step 4 (実際のレスポンス) + +サーバは `Access-Control-Allow-Origin` をレスポンスに追加するのを忘れないでください。それがないと、成功した preflight は解放しません。: + +``` +Access-Control-Allow-Origin: https://javascript.info +``` + +これですべてOKです。JavaScript は完全なレスポンスを読むことができます。 + + +## Credentials + +デフォルトでは、クロスオリジンリクエストはクレデンシャル(cookie or HTTP 認証)を持ちません。 + +これは HTTP リクエストでは一般的ではありません。通常 `http://site.com` へのリクエストはそのドメインのすべての cookie を伴います。しかし、JavaScript メソッドにより作られたクロスオリジンリクエストは例外です。 + +例えば、`fetch('http://another.com')` は cookie を送信しません。たとえ `another.com` ドメインに属するものであっても。 + +なぜでしょう? + +なぜなら、クレデンシャルを持つリクエストは匿名のリクエストよりもはるかに強力だからです。もし許可されていると、それは JavaScript にユーザに代わって機密情報にアクセスする全権限を与えることになります。 + +サーバは本当に `Origin` のページをそれほど信頼しているのでしょうか?クレデンシャルを持つリクエストが通過するための追加のヘッダが必要です。 + +クレデンシャルを有効にするには、オプション `credentials: "include"` を追加します。: + +```js +fetch('http://another.com', { + credentials: "include" +}); +``` + +これで `fetch` はリクエストと一緒に `another.com` からの cookie を送信します。 + +もしサーバがクレデンシャルを含むリクエストを受け入れたい場合は、`Access-Control-Allow-Origin` に加えて、レスポンスにヘッダ `Access-Control-Allow-Credentials: true` を追加する必要があります。 + +例: + +``` +200 OK +Access-Control-Allow-Origin: https://javascript.info +Access-Control-Allow-Credentials: true +``` + +注意してください: `Access-Control-Allow-Origin` はクレデンシャルを持つリクエストに対してアスタリスク `*` を使うことは禁止されています。上のように正確なオリジンでなければなりません。これはサーバが本当に信頼する相手を知っていることを保証するための追加の安全対策です。 + +## サマリ + +ネットワークメソッドはクロスオリジンリクエストを2種類に分けます: "単純" とその他です。 + +[単純リクエスト](http://www.w3.org/TR/cors/#terminology) は次の条件を満たさなければなりません: +- メソッド: GET, POST or HEAD. +- ヘッダ -- 次のものだけセットできます: + - `Accept` + - `Accept-Language` + - `Content-Language` + - `Content-Type`: `application/x-www-form-urlencoded`, `multipart/form-data` または `text/plain` のいずれか. + +本質的な違いは、単純リクエストは以前から `<form>` もしくは `<script>` タグを使用して行うことができました。その一方、単純でないものは、ブラウザでは長い間不可能だったことです。 + +実際の違いは、単純リクエストは `Origin` ヘッダとともにすぐに送信されますが、他のリクエストでは、ブラウザは許可を求めるために事前の "priflight" リクエストを行います。 + +**単純リクエストの場合:** + +- → ブラウザは送信元をもつ `Origin` ヘッダを送信します。 +- ← クレデンシャルを持たないリクエストの場合(デフォルト)、サーバは以下を設定する必要があります: + - `Access-Control-Allow-Origin` に `*` または `Origin` と同じ値 +- ← クレデンシャルを持つリクエストの場合、サーバは以下を設定する必要があります: + - `Access-Control-Allow-Origin` に `Origin` + - `Access-Control-Allow-Credentials` に `true` + +さらに、JavaScript で単純でないレスポンスヘッダ(下記以外)にアクセスしたい場合: +- `Cache-Control` +- `Content-Language` +- `Content-Type` +- `Expires` +- `Last-Modified` +- `Pragma` + +...サーバは許可されたものを `Access-Control-Expose-Headers` ヘッダにリストする必要があります。 + +**単純でないリクエストの場合、要求されたものの前に事前の "preflight" リクエストが発行されます。** + +- → ブラウザは次のヘッダと共に同じ URL に `OPTIONS` リクエストを送信します。: + - `Access-Control-Request-Method` は要求されたメソッドを持ちます + - `Access-Control-Request-Headers` は要求された単純でないヘッダのリストです +- ← サーバはステータス 200 と次のヘッダを応答します: + - `Access-Control-Allow-Methods` は許可されたメソッドのリストを含みます, + - `Access-Control-Allow-Headers` は許可されたヘッダのリストを含みます, + - `Access-Control-Max-Age` は許可(パーミッション)のキャッシュの秒数です。 +- その後、実際のリクエストが送信され、前の "単純な" スキームが適用されます。 diff --git a/5-network/05-fetch-crossorigin/cors-gmail-messages.svg b/5-network/05-fetch-crossorigin/cors-gmail-messages.svg new file mode 100644 index 0000000000..c24aac1401 --- /dev/null +++ b/5-network/05-fetch-crossorigin/cors-gmail-messages.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="651" height="285" viewBox="0 0 651 285"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="network" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="cors-gmail-messages.svg"><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M11 48h298v199H11z"/><g id="<script>-let-respons" fill-rule="nonzero" transform="translate(15.937 58.259)"><path id="<script>" fill="#7E7C7B" d="M5.339 9.099l-.745.752L0 6.016 4.594 2.18l.745.758-3.726 3.063L5.34 9.099zm7.95-1.23a1.634 1.634 0 01-.458 1.158 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199V8.511c.4.114.8.2 1.196.26.397.059.79.088 1.183.088.57 0 .991-.077 1.264-.232.274-.155.41-.376.41-.663a.812.812 0 00-.064-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.859-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.503-.358.868-.488.364-.13.82-.195 1.367-.195.269 0 .567.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.545.023-.748.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.023.235.071.335.048.1.137.197.267.29.13.094.311.187.543.28.233.094.536.196.91.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.372.54.075.2.113.428.113.683zm7.595 1.62c-.31.118-.628.205-.954.262a5.845 5.845 0 01-1.008.086c-1.085 0-1.92-.294-2.505-.882-.586-.588-.879-1.447-.879-2.577 0-.542.084-1.035.253-1.477a3.142 3.142 0 011.805-1.863c.424-.17.89-.256 1.401-.256.356 0 .688.025.998.075.31.05.606.133.889.247v1.134a3.77 3.77 0 00-.906-.338 4.248 4.248 0 00-.954-.106c-.305 0-.593.058-.864.174-.271.116-.51.284-.715.503a2.38 2.38 0 00-.485.8c-.119.314-.178.67-.178 1.066 0 .83.202 1.45.605 1.863.404.412.963.618 1.678.618a3.897 3.897 0 001.818-.438v1.108zm2.645-6.611h1.087l.034 1.265c.406-.488.806-.841 1.2-1.06a2.436 2.436 0 011.193-.328c.71 0 1.25.23 1.617.69.367.46.536 1.144.509 2.051h-1.203c.013-.601-.074-1.038-.263-1.309-.19-.271-.466-.407-.83-.407-.16 0-.321.029-.483.086a1.947 1.947 0 00-.499.273 4.47 4.47 0 00-.543.482 8.898 8.898 0 00-.616.711v4.41H23.53V2.877zm9.755.984h-2.03v-.984h3.233V8.75h2.044v.991h-5.503V8.75h2.256V3.862zM33.701 0c.132 0 .255.024.37.072a.893.893 0 01.297.201.919.919 0 01.27.663.96.96 0 01-.27.663.893.893 0 01-.298.202.944.944 0 01-.369.072.944.944 0 01-.369-.072.893.893 0 01-.297-.201.96.96 0 01-.27-.663.919.919 0 01.27-.664.893.893 0 01.297-.201.944.944 0 01.37-.072zM44.51 6.187c0 .61-.086 1.143-.257 1.6-.17.455-.406.833-.707 1.134a2.9 2.9 0 01-1.066.677c-.41.15-.855.225-1.333.225-.22 0-.437-.011-.653-.034a4.952 4.952 0 01-.66-.116v2.87h-1.19V2.879h1.06l.075 1.148c.342-.469.707-.798 1.094-.987.387-.19.807-.284 1.258-.284.392 0 .736.082 1.032.246.296.164.545.395.745.694.2.298.351.658.451 1.08.1.421.15.892.15 1.412zm-1.217.054c0-.36-.026-.69-.079-.991a2.586 2.586 0 00-.25-.772 1.37 1.37 0 00-.437-.503 1.104 1.104 0 00-.635-.181c-.15 0-.303.024-.458.072a1.857 1.857 0 00-.482.239 3.59 3.59 0 00-.527.444 7 7 0 00-.591.687v3.33c.219.09.449.162.69.215.242.052.479.078.711.078.643 0 1.147-.217 1.511-.652.365-.436.547-1.09.547-1.966zm8.58 3.405a5.793 5.793 0 01-.835.146 8.52 8.52 0 01-.875.045c-.861 0-1.504-.195-1.928-.585-.423-.39-.635-.987-.635-1.794V3.876h-1.921v-.998h1.92V.99l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.131.86.393 1.104.262.244.648.366 1.159.366.219 0 .458-.017.718-.051.26-.034.53-.088.813-.16v1.025zm2.501-6.707l.738-.758 4.594 3.835-4.594 3.835-.738-.752 3.726-3.07-3.726-3.09z"/><path id="let" fill="#1C85B5" d="M2.495 33.06H.465v-.978h3.233v8.668h2.044v.991H.24v-.991h2.256v-7.69zm11.156 4.887c0 .169-.002.31-.006.424a6.37 6.37 0 01-.021.321h-4.82c0 .702.197 1.241.589 1.617.392.376.957.564 1.695.564.2 0 .4-.008.601-.024.201-.016.395-.037.582-.065.186-.027.365-.058.536-.092.171-.034.33-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.194-1.494c0-.483.069-.94.208-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.271.225.37.15.68.364.933.64.253.275.445.61.575 1.005.13.394.194.835.194 1.322zm-1.237-.17a2.593 2.593 0 00-.089-.838 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.571-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.71.15-.215.1-.4.242-.555.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.177.84h3.61zm8.668 3.869a5.793 5.793 0 01-.834.146 8.52 8.52 0 01-.875.045c-.861 0-1.504-.195-1.928-.585-.424-.39-.635-.987-.635-1.794v-3.582h-1.921v-.998h1.92V32.99l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.131.86.393 1.104.262.244.648.366 1.159.366.219 0 .458-.017.718-.051.26-.034.53-.088.813-.16v1.025z"/><path id="response" fill="#181717" d="M31.227 34.878h1.086l.035 1.265c.405-.488.805-.841 1.2-1.06a2.436 2.436 0 011.192-.328c.711 0 1.25.23 1.617.69.367.46.537 1.144.51 2.051h-1.204c.014-.601-.074-1.038-.263-1.309-.19-.271-.466-.407-.83-.407-.16 0-.32.029-.483.086a1.947 1.947 0 00-.499.273 4.47 4.47 0 00-.543.482 8.898 8.898 0 00-.615.711v4.41h-1.203v-6.864zm13.213 3.07c0 .168-.002.31-.006.423a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.2 0 .401-.008.602-.024.2-.016.394-.037.58-.065.187-.027.366-.058.537-.092.171-.034.33-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.194-1.494c0-.483.07-.94.208-1.37a3.44 3.44 0 01.609-1.135c.266-.326.593-.585.98-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.271.225.37.15.68.364.933.64.253.275.445.61.575 1.005.13.394.194.835.194 1.322zm-1.237-.172a2.593 2.593 0 00-.089-.837 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.794-.154c-.26 0-.497.05-.71.15-.215.1-.4.242-.555.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.177.84h3.61zm8.572 2.092a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.401.114.8.2 1.197.26.396.059.79.088 1.182.088.57 0 .991-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.859-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.45-.56 1.649 1.649 0 01-.165-.759 1.744 1.744 0 01.588-1.265c.214-.195.504-.358.868-.488.365-.13.82-.195 1.367-.195.27 0 .568.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.871-.065c-.297 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.136.197.266.29.13.094.311.187.544.28.232.094.535.196.909.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.373.54.075.2.112.428.112.683zm8.128-1.681c0 .61-.085 1.143-.256 1.6-.17.455-.407.833-.708 1.134a2.9 2.9 0 01-1.066.677c-.41.15-.854.225-1.333.225-.219 0-.436-.011-.653-.034a4.952 4.952 0 01-.66-.116v2.87h-1.189v-9.665h1.06l.075 1.148c.342-.469.706-.798 1.094-.987.387-.19.806-.284 1.257-.284.392 0 .736.082 1.033.246.296.164.544.395.745.694.2.298.35.658.45 1.08.101.421.151.892.151 1.412zm-1.216.054c0-.36-.027-.69-.08-.991a2.586 2.586 0 00-.249-.772 1.37 1.37 0 00-.437-.503 1.104 1.104 0 00-.636-.181c-.15 0-.303.024-.458.072a1.857 1.857 0 00-.482.239 3.59 3.59 0 00-.526.444 7 7 0 00-.591.687v3.33c.218.09.448.162.69.215.242.052.478.078.71.078.643 0 1.147-.217 1.512-.652.364-.436.547-1.09.547-1.966zm9.023.014a4.55 4.55 0 01-.226 1.466c-.15.445-.366.825-.649 1.142a2.913 2.913 0 01-1.032.738 3.447 3.447 0 01-1.381.263c-.492 0-.933-.076-1.323-.229a2.617 2.617 0 01-.991-.673 2.98 2.98 0 01-.622-1.1c-.144-.438-.215-.942-.215-1.511 0-.534.075-1.02.225-1.46.15-.44.367-.818.65-1.135a2.913 2.913 0 011.032-.738 3.447 3.447 0 011.38-.263c.493 0 .934.076 1.323.229.39.153.72.376.992.67.27.294.478.66.622 1.097.143.437.215.939.215 1.504zm-1.217.055c0-.424-.047-.795-.14-1.111a2.237 2.237 0 00-.4-.793 1.65 1.65 0 00-.632-.479 2.077 2.077 0 00-.83-.16c-.356 0-.66.07-.913.208-.253.14-.46.325-.622.557a2.4 2.4 0 00-.356.81 4.06 4.06 0 00-.113.968c0 .423.047.795.14 1.114.094.319.227.584.4.796.174.212.383.372.63.479.245.107.523.16.833.16.356 0 .66-.07.913-.208.253-.14.46-.325.622-.557a2.4 2.4 0 00.355-.81 4.09 4.09 0 00.113-.974zm2.94-3.432h1.06l.047 1.107c.2-.237.394-.434.581-.591.187-.157.37-.284.55-.38a2.06 2.06 0 01.55-.201c.188-.039.381-.058.582-.058.706 0 1.24.208 1.603.625.362.417.543 1.045.543 1.884v4.477h-1.19V37.36c0-.537-.1-.935-.3-1.192-.2-.258-.499-.387-.896-.387-.145 0-.288.022-.427.065a1.557 1.557 0 00-.434.226c-.15.107-.313.252-.489.434-.175.182-.372.41-.59.683v4.553h-1.19v-6.863zm13.131 4.99a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.401.114.8.2 1.197.26.396.059.79.088 1.182.088.57 0 .991-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.452-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.504-.358.868-.488.365-.13.82-.195 1.367-.195.27 0 .568.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.871-.065c-.297 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.136.197.266.29.13.094.311.187.544.28.232.094.535.196.909.305.406.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.373.54.075.2.112.428.112.683zm8.06-1.92c0 .168-.002.31-.007.423a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.696.564.2 0 .4-.008.601-.024.2-.016.394-.037.581-.065.187-.027.366-.058.537-.092.17-.034.329-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.209-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.27.225.37.15.681.364.934.64.253.275.444.61.574 1.005.13.394.195.835.195 1.322zm-1.237-.172a2.593 2.593 0 00-.09-.837 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.711.15-.214.1-.399.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.178.84h3.61z"/><path id="=" fill="#DBAF88" d="M105.916 37.325h-5.879V36.32h5.879v1.005zm0 2.38h-5.879v-1.006h5.879v1.005z"/><path id="await" fill="#1C85B5" d="M120.025 41.741l-.027-.923c-.374.37-.753.636-1.138.8-.385.164-.79.246-1.214.246-.391 0-.726-.05-1.004-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.397-.618 2.289 2.289 0 01-.126-.773c0-.688.256-1.227.769-1.617.512-.39 1.27-.584 2.273-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.19-.365-.387 0-.769.043-1.145.13-.376.086-.764.21-1.165.369v-1.074a7.36 7.36 0 011.087-.3 7.6 7.6 0 01.642-.1c.224-.025.45-.037.677-.037.415 0 .789.045 1.121.137.333.09.614.23.844.417.23.186.407.421.53.704.123.282.185.615.185.998v4.73h-1.067zm-.13-3.124h-1.51c-.296 0-.552.03-.766.09-.214.058-.39.143-.526.252-.137.11-.238.24-.304.393-.067.153-.1.325-.1.516 0 .132.021.259.062.38.041.12.107.227.198.32.091.094.21.168.356.223.146.055.323.082.533.082.273 0 .587-.083.94-.25.353-.166.726-.429 1.118-.789v-1.217zm9.776-3.74l-.998 6.864h-1.443l-.99-2.87-.199-.698-.226.738-.95 2.83h-1.401l-.991-6.863h1.162l.574 4.662.123 1.04.294-.91.998-3.083h.855l1.073 3.042.307.91.103-.965.533-4.696h1.176zm5.749 6.864l-.027-.923c-.374.37-.754.636-1.139.8-.385.164-.79.246-1.213.246-.392 0-.727-.05-1.005-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.396-.618 2.289 2.289 0 01-.127-.773c0-.688.257-1.227.77-1.617.512-.39 1.27-.584 2.272-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.19-.365-.386 0-.768.043-1.144.13-.376.086-.765.21-1.166.369v-1.074a7.36 7.36 0 011.087-.3 7.6 7.6 0 01.643-.1c.223-.025.449-.037.677-.037.414 0 .788.045 1.12.137.333.09.615.23.845.417.23.186.407.421.53.704.123.282.184.615.184.998v4.73h-1.066zm-.13-3.124h-1.51c-.297 0-.552.03-.766.09-.215.058-.39.143-.527.252-.136.11-.238.24-.304.393-.066.153-.099.325-.099.516 0 .132.02.259.062.38.04.12.107.227.198.32.09.094.21.168.355.223.146.055.324.082.533.082.274 0 .587-.083.94-.25.354-.166.726-.429 1.118-.789v-1.217zm5.756-2.755h-2.03v-.984h3.233v5.872h2.044v.991h-5.503v-.991h2.256v-4.888zm.417-3.862c.132 0 .255.024.369.072a.893.893 0 01.297.201.919.919 0 01.27.663.96.96 0 01-.27.663.893.893 0 01-.297.202.944.944 0 01-.37.072.944.944 0 01-.368-.072.893.893 0 01-.298-.201.96.96 0 01-.27-.663.919.919 0 01.27-.664.893.893 0 01.298-.201.944.944 0 01.369-.072zm10.473 9.646a5.793 5.793 0 01-.834.146 8.52 8.52 0 01-.875.045c-.862 0-1.504-.195-1.928-.585-.424-.39-.636-.987-.636-1.794v-3.582h-1.92v-.998h1.92V32.99l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.13.86.393 1.104.262.244.648.366 1.158.366.219 0 .458-.017.718-.051.26-.034.531-.088.814-.16v1.025z"/><path id="fetch" fill="#181717" d="M167.89 33.19c-.624-.133-1.161-.199-1.613-.199-1.07 0-1.606.56-1.606 1.682v1.203h3.008v.991h-3.008v4.874h-1.21v-4.874h-2.208v-.991h2.208v-1.135c0-1.827.952-2.741 2.857-2.741.474 0 .998.055 1.573.164v1.025zm7.404 4.757c0 .169-.002.31-.007.424a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.201 0 .402-.008.602-.024.2-.016.394-.037.581-.065.187-.027.366-.058.537-.092.17-.034.329-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.209-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.27.225.37.15.681.364.934.64.253.275.444.61.574 1.005.13.394.195.835.195 1.322zm-1.237-.17a2.593 2.593 0 00-.09-.838 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.711.15-.215.1-.4.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.178.84h3.61zm8.668 3.869a5.793 5.793 0 01-.834.146 8.52 8.52 0 01-.875.045c-.862 0-1.504-.195-1.928-.585-.424-.39-.636-.987-.636-1.794v-3.582h-1.92v-.998h1.92V32.99l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.13.86.393 1.104.262.244.648.366 1.158.366.22 0 .458-.017.718-.051.26-.034.531-.088.814-.16v1.025zm7.499-.158c-.31.119-.628.206-.954.263a5.845 5.845 0 01-1.008.086c-1.085 0-1.92-.294-2.506-.882-.585-.588-.878-1.447-.878-2.577 0-.542.084-1.035.253-1.477a3.142 3.142 0 011.805-1.863c.423-.17.89-.256 1.4-.256.356 0 .69.025.999.075.31.05.606.133.889.247v1.134a3.77 3.77 0 00-.906-.338 4.248 4.248 0 00-.954-.106c-.305 0-.593.058-.864.174-.272.116-.51.284-.715.503a2.38 2.38 0 00-.485.8c-.119.314-.178.67-.178 1.066 0 .83.202 1.45.605 1.863.403.412.963.618 1.678.618a3.897 3.897 0 001.818-.438v1.108zm7.881.253h-1.189V37.36c0-.528-.1-.924-.297-1.186-.199-.262-.482-.393-.851-.393-.16 0-.31.022-.448.065a1.557 1.557 0 00-.434.226 3.73 3.73 0 00-.492.434c-.178.182-.383.41-.616.683v4.553h-1.19v-9.659h1.19v2.796l-.04 1.08c.186-.223.37-.411.55-.564.18-.153.36-.277.54-.373.18-.095.363-.164.55-.205.187-.04.38-.061.581-.061.684 0 1.212.208 1.586.625.374.417.56 1.045.56 1.884v4.477z"/><path id="(" fill="#7E7C7B" d="M204.395 44.612c-2.101-1.946-3.152-4.097-3.152-6.453 0-.551.056-1.102.168-1.65.111-.55.291-1.1.54-1.652a9.028 9.028 0 01.984-1.654c.408-.551.904-1.098 1.487-1.64l.69.704c-1.768 1.745-2.652 3.68-2.652 5.803 0 1.058.223 2.074.67 3.05.447.974 1.107 1.895 1.982 2.76l-.717.732z"/><path id="'https://gmail.com/messages'" fill="#478964" d="M19.093 46.082l-.198 3.206h-1.176l-.192-3.206h1.566zm9.673 9.66h-1.19v-4.383c0-.528-.099-.924-.297-1.186-.198-.262-.482-.393-.851-.393-.16 0-.309.022-.448.065a1.557 1.557 0 00-.434.226 3.73 3.73 0 00-.492.434c-.178.182-.383.41-.616.683v4.553H23.25v-9.659h1.19v2.796l-.042 1.08c.187-.223.37-.411.55-.564.18-.153.36-.277.54-.373.18-.095.364-.164.551-.205.187-.04.38-.061.581-.061.684 0 1.212.208 1.586.625.374.417.56 1.045.56 1.884v4.477zm7.71-.096a5.793 5.793 0 01-.833.146 8.52 8.52 0 01-.875.045c-.862 0-1.504-.195-1.928-.585-.424-.39-.636-.987-.636-1.794v-3.582h-1.92v-.998h1.92V46.99l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.13.86.393 1.104.262.244.648.366 1.158.366.22 0 .458-.017.718-.051.26-.034.531-.088.814-.16v1.025zm7.698 0a5.793 5.793 0 01-.834.146 8.52 8.52 0 01-.875.045c-.861 0-1.504-.195-1.928-.585-.424-.39-.636-.987-.636-1.794v-3.582h-1.92v-.998h1.92V46.99l1.19-.307v2.194h3.083v.998H41.09v3.486c0 .492.13.86.393 1.104.262.244.648.366 1.159.366.218 0 .458-.017.717-.051.26-.034.531-.088.814-.16v1.025zm8.032-3.46c0 .611-.085 1.144-.256 1.6-.171.456-.407.834-.708 1.135a2.9 2.9 0 01-1.066.677c-.41.15-.855.225-1.333.225-.219 0-.437-.011-.653-.034a4.952 4.952 0 01-.66-.116v2.87h-1.19v-9.665h1.06l.076 1.148c.341-.469.706-.798 1.093-.987.388-.19.807-.284 1.258-.284.392 0 .736.082 1.032.246.297.164.545.395.745.694.201.298.351.658.452 1.08.1.421.15.892.15 1.412zm-1.217.055c0-.36-.026-.69-.078-.991a2.586 2.586 0 00-.25-.772 1.37 1.37 0 00-.437-.503 1.104 1.104 0 00-.636-.181c-.15 0-.303.024-.458.072a1.857 1.857 0 00-.482.239 3.59 3.59 0 00-.526.444 7 7 0 00-.592.687v3.33c.219.09.45.162.69.215.242.052.48.078.712.078.642 0 1.146-.217 1.51-.652.365-.436.547-1.09.547-1.966zm8.484 1.627a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.493.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.402.114.8.2 1.197.26.396.059.79.088 1.182.088.57 0 .992-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.452-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.504-.358.868-.488.365-.13.82-.195 1.368-.195.268 0 .567.015.895.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.871-.065c-.296 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.137.197.267.29.13.094.31.187.543.28.232.094.535.196.91.305.405.118.747.243 1.025.372.278.13.503.275.676.435.173.159.298.339.373.54.075.2.113.428.113.683zm5.017-5.113c.142 0 .276.028.404.085a1.107 1.107 0 01.564.564.978.978 0 01.085.404.971.971 0 01-.085.406 1.12 1.12 0 01-.23.332 1.058 1.058 0 01-.738.308c-.146 0-.281-.028-.407-.083a1.07 1.07 0 01-.557-.557 1.006 1.006 0 01-.082-.406 1.058 1.058 0 01.308-.738 1.12 1.12 0 01.331-.23.971.971 0 01.407-.085zm0 5.01c.142 0 .276.03.404.086a1.107 1.107 0 01.564.564.978.978 0 01.085.403.971.971 0 01-.085.407 1.12 1.12 0 01-.23.332 1.058 1.058 0 01-.738.307c-.146 0-.281-.027-.407-.082a1.07 1.07 0 01-.557-.557 1.006 1.006 0 01-.082-.407 1.058 1.058 0 01.308-.738 1.12 1.12 0 01.331-.229.971.971 0 01.407-.085zm10.46-7.683l-4.69 11.129h-1.135l4.69-11.129h1.134zm7.696 0l-4.689 11.129h-1.135l4.69-11.129h1.134zm7.124 3.767c.127.16.226.345.297.557.07.212.106.44.106.687 0 .355-.065.68-.195.974-.13.294-.313.546-.55.755a2.54 2.54 0 01-.851.489c-.33.116-.694.174-1.09.174-.288 0-.557-.03-.807-.092a2.064 2.064 0 01-.595-.229c-.087.128-.16.248-.219.362a.817.817 0 00-.089.383c0 .173.084.317.25.43.166.115.386.176.66.185l1.804.069c.342.009.658.052.947.13.29.077.538.189.745.335.207.145.37.325.485.54.117.214.175.462.175.745 0 .305-.066.594-.198.868a1.985 1.985 0 01-.612.721c-.276.207-.626.373-1.05.496-.423.123-.927.184-1.51.184-.556 0-1.03-.044-1.419-.133-.39-.089-.71-.212-.96-.37a1.49 1.49 0 01-.547-.56 1.51 1.51 0 01-.171-.714c0-.333.077-.624.232-.875.155-.25.395-.492.718-.725a1.133 1.133 0 01-.52-.475 1.322 1.322 0 01-.164-.632c0-.296.07-.567.21-.814.138-.246.303-.478.495-.697a3.57 3.57 0 01-.23-.307 1.91 1.91 0 01-.28-.701 2.536 2.536 0 01-.037-.462c0-.355.065-.68.195-.974.13-.294.312-.545.547-.755.234-.21.517-.373.847-.489.33-.116.696-.174 1.097-.174.169 0 .33.011.486.034.155.023.291.052.41.089h2.488v.97h-1.1zm-4.13 6.883c0 .324.17.56.506.708.338.148.807.222 1.409.222.378 0 .696-.034.953-.102.258-.069.465-.159.622-.27.158-.112.27-.24.339-.383.068-.144.102-.29.102-.441 0-.278-.114-.483-.342-.615-.227-.133-.576-.21-1.045-.233l-1.791-.061c-.15.1-.275.198-.373.294a1.292 1.292 0 00-.229.29 1.127 1.127 0 00-.116.294 1.309 1.309 0 00-.034.297zm.363-5.612c0 .219.036.42.11.602.072.182.175.337.307.465.132.127.288.226.468.297.18.07.38.106.598.106.237 0 .448-.04.633-.12.184-.08.34-.188.464-.324.126-.137.222-.294.288-.472.066-.178.099-.362.099-.554 0-.219-.037-.42-.11-.601a1.316 1.316 0 00-.307-.465 1.404 1.404 0 00-.469-.298c-.18-.07-.379-.106-.598-.106-.237 0-.447.041-.632.123-.185.082-.34.19-.465.325s-.22.29-.287.468c-.066.178-.1.363-.1.554zm11.416 4.621v-4.928c0-.215-.008-.39-.024-.527a1.2 1.2 0 00-.075-.325.348.348 0 00-.13-.167.43.43 0 00-.448.034c-.077.055-.16.144-.25.267a4.216 4.216 0 00-.293.488c-.107.203-.234.453-.38.749v4.41h-1.087v-4.8c0-.25-.008-.453-.024-.608a1.42 1.42 0 00-.075-.362.34.34 0 00-.133-.178.431.431 0 00-.43.02.988.988 0 00-.243.247c-.09.118-.188.28-.298.485-.11.205-.239.467-.39.786v4.41h-1.093v-6.864h.91l.054 1.306c.118-.26.233-.481.345-.663.112-.183.227-.33.345-.441.119-.112.245-.194.38-.247.134-.052.283-.078.447-.078.37 0 .65.12.841.362.192.242.287.615.287 1.121.11-.237.217-.447.322-.632a2.31 2.31 0 01.338-.465c.12-.125.254-.22.4-.287.146-.066.314-.1.506-.1.861 0 1.292.664 1.292 1.99v4.997h-1.094zm7.212 0l-.027-.923c-.374.37-.754.636-1.139.8-.385.164-.79.246-1.213.246-.392 0-.727-.05-1.005-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.396-.618 2.289 2.289 0 01-.127-.773c0-.688.256-1.227.77-1.617.512-.39 1.27-.584 2.272-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.19-.365-.386 0-.768.043-1.144.13-.376.086-.765.21-1.166.369v-1.074a7.36 7.36 0 011.087-.3 7.6 7.6 0 01.643-.1c.223-.025.449-.037.677-.037.414 0 .788.045 1.12.137.333.09.615.23.845.417.23.186.407.421.53.704.123.282.184.615.184.998v4.73h-1.066zm-.13-3.124h-1.51c-.297 0-.552.03-.766.09-.215.058-.39.143-.527.252-.136.11-.238.24-.304.393-.066.153-.1.325-.1.516 0 .132.021.259.062.38.041.12.108.227.199.32.09.094.21.168.355.223.146.055.324.082.533.082.274 0 .587-.083.94-.25.353-.166.726-.429 1.118-.789v-1.217zm5.756-2.755h-2.03v-.984h3.233v5.872h2.044v.991H108v-.991h2.256v-4.888zm.417-3.862c.132 0 .255.024.369.072a.893.893 0 01.297.201.919.919 0 01.27.663.96.96 0 01-.27.663.893.893 0 01-.297.202.944.944 0 01-.37.072.944.944 0 01-.368-.072.893.893 0 01-.298-.201.96.96 0 01-.27-.663.919.919 0 01.27-.664.893.893 0 01.298-.201.944.944 0 01.369-.072zm7.28 1.06h-2.03v-.978h3.233v8.668h2.044v.991h-5.503v-.991h2.256v-7.69zm8.06 6.528a1.113 1.113 0 01.803.335c.102.105.183.227.243.366.059.139.088.288.088.447 0 .155-.03.301-.088.438a1.148 1.148 0 01-.605.601c-.14.06-.286.09-.441.09-.16 0-.308-.03-.445-.09a1.148 1.148 0 01-.601-.601 1.088 1.088 0 01-.09-.438 1.178 1.178 0 01.332-.813 1.12 1.12 0 01.804-.335zm10.329 1.9c-.31.119-.628.206-.954.263a5.845 5.845 0 01-1.008.086c-1.085 0-1.92-.294-2.506-.882-.585-.588-.878-1.447-.878-2.577 0-.542.084-1.035.253-1.477.169-.442.406-.82.71-1.134.306-.315.67-.558 1.095-.728.424-.171.89-.257 1.401-.257.356 0 .688.025.998.075.31.05.606.133.889.247v1.134a3.77 3.77 0 00-.906-.338 4.248 4.248 0 00-.954-.106c-.305 0-.593.058-.864.174-.272.116-.51.284-.715.503a2.38 2.38 0 00-.485.8c-.119.314-.178.67-.178 1.066 0 .83.202 1.45.605 1.863.404.412.963.618 1.678.618a3.897 3.897 0 001.818-.438v1.108zm8.34-3.233a4.55 4.55 0 01-.226 1.466c-.15.445-.367.825-.65 1.142a2.913 2.913 0 01-1.032.738 3.447 3.447 0 01-1.38.263c-.493 0-.934-.076-1.323-.229a2.617 2.617 0 01-.991-.673 2.98 2.98 0 01-.623-1.1c-.143-.438-.215-.942-.215-1.511 0-.534.075-1.02.226-1.46.15-.44.367-.818.65-1.135a2.913 2.913 0 011.031-.738 3.447 3.447 0 011.381-.263c.492 0 .933.076 1.323.229.39.153.72.376.991.67.271.294.479.66.622 1.097.144.437.216.939.216 1.504zm-1.217.055c0-.424-.047-.795-.14-1.111a2.237 2.237 0 00-.4-.793 1.65 1.65 0 00-.633-.479 2.077 2.077 0 00-.83-.16c-.356 0-.66.07-.913.208-.253.14-.46.325-.622.557a2.4 2.4 0 00-.355.81 4.06 4.06 0 00-.113.968c0 .423.047.795.14 1.114.094.319.227.584.4.796.173.212.383.372.629.479.246.107.524.16.834.16.355 0 .66-.07.912-.208.253-.14.46-.325.623-.557a2.4 2.4 0 00.355-.81 4.09 4.09 0 00.113-.974zm7.834 3.431v-4.928c0-.215-.008-.39-.024-.527a1.2 1.2 0 00-.075-.325.348.348 0 00-.13-.167.43.43 0 00-.448.034c-.077.055-.16.144-.25.267a4.216 4.216 0 00-.293.488 30.3 30.3 0 00-.38.749v4.41h-1.087v-4.8c0-.25-.008-.453-.024-.608a1.42 1.42 0 00-.075-.362.34.34 0 00-.133-.178.431.431 0 00-.43.02.988.988 0 00-.243.247c-.09.118-.188.28-.298.485-.11.205-.24.467-.39.786v4.41h-1.093v-6.864h.909l.055 1.306c.118-.26.233-.481.345-.663.111-.183.227-.33.345-.441.119-.112.245-.194.38-.247.134-.052.283-.078.447-.078.37 0 .65.12.841.362.191.242.287.615.287 1.121.11-.237.217-.447.321-.632a2.31 2.31 0 01.339-.465c.12-.125.254-.22.4-.287.146-.066.314-.1.506-.1.86 0 1.292.664 1.292 1.99v4.997H151.3zm8.32-9.659l-4.69 11.129h-1.135l4.69-11.129h1.134zm7.074 9.66v-4.93c0-.214-.008-.39-.024-.526a1.2 1.2 0 00-.075-.325.348.348 0 00-.13-.167.43.43 0 00-.448.034c-.077.055-.16.144-.249.267a4.216 4.216 0 00-.294.488c-.107.203-.233.453-.38.749v4.41h-1.086v-4.8c0-.25-.008-.453-.024-.608a1.42 1.42 0 00-.075-.362.34.34 0 00-.134-.178.431.431 0 00-.43.02.988.988 0 00-.243.247c-.089.118-.188.28-.297.485-.11.205-.24.467-.39.786v4.41h-1.094v-6.864h.91l.054 1.306c.119-.26.234-.481.345-.663.112-.183.227-.33.346-.441.118-.112.245-.194.379-.247.134-.052.284-.078.448-.078.369 0 .65.12.84.362.192.242.288.615.288 1.121.109-.237.216-.447.321-.632a2.31 2.31 0 01.338-.465c.121-.125.254-.22.4-.287.146-.066.315-.1.506-.1.861 0 1.292.664 1.292 1.99v4.997h-1.094zm8.6-3.795c0 .169-.002.31-.007.424a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.201 0 .402-.008.602-.024.2-.016.394-.037.581-.065.187-.027.366-.058.537-.092.17-.034.329-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.209-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.27.225.37.15.681.364.934.64.253.275.444.61.574 1.005.13.394.195.835.195 1.322zm-1.237-.17a2.593 2.593 0 00-.09-.838 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.711.15-.215.1-.4.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.178.84h3.61zm8.572 2.091a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.657.048 12.3 12.3 0 01-1.281-.061 8.646 8.646 0 01-1.145-.199v-1.093c.4.114.8.2 1.196.26.397.059.79.088 1.183.088.57 0 .99-.077 1.264-.232.274-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.235-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.981-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.587-1.265c.215-.195.504-.358.869-.488.364-.13.82-.195 1.367-.195.269 0 .567.015.895.044.328.03.67.081 1.026.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.546.023-.748.068a1.553 1.553 0 00-.493.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.137.197.267.29.13.094.31.187.543.28.233.094.536.196.91.305.405.118.747.243 1.025.372.278.13.503.275.677.435.173.159.297.339.372.54.075.2.113.428.113.683zm7.697 0a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.401.114.8.2 1.196.26.397.059.791.088 1.183.088.57 0 .991-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.859-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.503-.358.868-.488.364-.13.82-.195 1.367-.195.269 0 .567.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.545.023-.748.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.071.335.048.1.137.197.267.29.13.094.311.187.543.28.233.094.536.196.91.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.372.54.076.2.113.428.113.683zm6.672 1.873l-.027-.923c-.374.37-.753.636-1.138.8-.386.164-.79.246-1.214.246-.392 0-.727-.05-1.005-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.396-.618 2.289 2.289 0 01-.127-.773c0-.688.257-1.227.77-1.617.512-.39 1.27-.584 2.272-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.189-.365-.387 0-.769.043-1.145.13-.376.086-.764.21-1.165.369v-1.074a7.36 7.36 0 011.086-.3 7.6 7.6 0 01.643-.1c.223-.025.449-.037.677-.037.414 0 .788.045 1.12.137.334.09.615.23.845.417.23.186.407.421.53.704.123.282.184.615.184.998v4.73h-1.066zm-.13-3.124h-1.51c-.297 0-.552.03-.766.09-.214.058-.39.143-.527.252-.136.11-.238.24-.304.393-.066.153-.099.325-.099.516 0 .132.02.259.062.38.04.12.107.227.198.32.091.094.21.168.355.223.146.055.324.082.534.082.273 0 .586-.083.94-.25.353-.166.725-.429 1.117-.789v-1.217zm8.36-2.768c.128.16.227.345.298.557.07.212.106.44.106.687 0 .355-.065.68-.195.974-.13.294-.313.546-.55.755a2.54 2.54 0 01-.851.489c-.33.116-.694.174-1.09.174-.288 0-.557-.03-.807-.092a2.064 2.064 0 01-.595-.229c-.087.128-.16.248-.219.362a.817.817 0 00-.089.383c0 .173.083.317.25.43.166.115.386.176.66.185l1.804.069c.342.009.658.052.947.13.29.077.538.189.745.335.207.145.37.325.485.54.117.214.175.462.175.745 0 .305-.066.594-.198.868a1.985 1.985 0 01-.612.721c-.276.207-.626.373-1.05.496-.423.123-.927.184-1.51.184-.556 0-1.03-.044-1.419-.133-.39-.089-.71-.212-.96-.37a1.49 1.49 0 01-.547-.56 1.51 1.51 0 01-.171-.714c0-.333.077-.624.232-.875.155-.25.395-.492.718-.725a1.133 1.133 0 01-.52-.475 1.322 1.322 0 01-.164-.632c0-.296.07-.567.21-.814.138-.246.303-.478.495-.697a3.57 3.57 0 01-.23-.307 1.91 1.91 0 01-.28-.701 2.536 2.536 0 01-.037-.462c0-.355.065-.68.195-.974.13-.294.312-.545.546-.755.235-.21.518-.373.848-.489.33-.116.696-.174 1.097-.174.169 0 .33.011.486.034.155.023.291.052.41.089h2.488v.97h-1.1zm-4.128 6.883c0 .324.168.56.505.708.338.148.807.222 1.409.222.378 0 .696-.034.953-.102.258-.069.465-.159.622-.27.158-.112.27-.24.339-.383.068-.144.102-.29.102-.441 0-.278-.114-.483-.342-.615-.227-.133-.576-.21-1.045-.233l-1.791-.061c-.15.1-.275.198-.373.294a1.292 1.292 0 00-.229.29 1.127 1.127 0 00-.116.294 1.309 1.309 0 00-.034.297zm.362-5.612c0 .219.036.42.11.602.072.182.175.337.307.465.132.127.288.226.468.297.18.07.38.106.598.106.237 0 .448-.04.633-.12.184-.08.34-.188.464-.324.126-.137.222-.294.288-.472.066-.178.099-.362.099-.554 0-.219-.037-.42-.11-.601a1.316 1.316 0 00-.307-.465 1.404 1.404 0 00-.469-.298c-.18-.07-.379-.106-.598-.106-.237 0-.447.041-.632.123-.185.082-.34.19-.465.325s-.22.29-.287.468c-.066.178-.1.363-.1.554zm12.318.827c0 .169-.002.31-.007.424a6.372 6.372 0 01-.02.321h-4.82c0 .702.197 1.241.588 1.617.392.376.958.564 1.696.564.2 0 .4-.008.601-.024.2-.016.395-.037.581-.065.187-.027.366-.058.537-.092.17-.034.33-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.209-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.271.225.37.15.68.364.933.64.253.275.445.61.574 1.005.13.394.195.835.195 1.322zm-1.237-.17a2.593 2.593 0 00-.089-.838 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.571-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.711.15-.214.1-.399.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.177.84h3.609zm8.572 2.091a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.637 4.637 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.401.114.8.2 1.196.26.397.059.791.088 1.183.088.57 0 .991-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.859-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.503-.358.868-.488s.82-.195 1.367-.195c.269 0 .568.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.545.023-.748.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.071.335.048.1.137.197.267.29.13.094.311.187.544.28.232.094.535.196.909.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.372.54.076.2.113.428.113.683zm5.804-7.786l-.198 3.206h-1.176l-.191-3.206h1.565z"/><path id=");" fill="#7E7C7B" d="M1.559 59.563c2.1 1.945 3.151 4.11 3.151 6.494 0 .492-.05 1.002-.15 1.53-.1.53-.272 1.07-.513 1.624a9.296 9.296 0 01-.974 1.685c-.408.57-.922 1.142-1.542 1.716l-.69-.704c.888-.88 1.552-1.798 1.99-2.755.437-.957.655-1.96.655-3.007 0-2.17-.881-4.12-2.645-5.852l.718-.731zm6.91 11.58c.233.009.459-.012.677-.062a2.02 2.02 0 00.578-.222c.166-.098.3-.22.4-.366a.86.86 0 00.15-.499.95.95 0 00-.099-.465 2.238 2.238 0 00-.222-.328 2.438 2.438 0 01-.222-.321.91.91 0 01-.1-.458.915.915 0 01.223-.588.857.857 0 01.287-.212 1.12 1.12 0 01.875.017c.148.066.278.168.39.304.111.137.2.308.266.513.066.205.1.447.1.725 0 .378-.07.741-.209 1.09a2.657 2.657 0 01-.622.926c-.276.27-.62.483-1.032.643-.413.16-.892.24-1.44.24v-.937zm2.14-8.388c.142 0 .276.028.404.085a1.107 1.107 0 01.564.564.978.978 0 01.085.404.971.971 0 01-.085.406 1.12 1.12 0 01-.23.332 1.058 1.058 0 01-.738.308c-.145 0-.281-.028-.406-.083a1.07 1.07 0 01-.557-.557 1.006 1.006 0 01-.083-.406 1.058 1.058 0 01.308-.738 1.12 1.12 0 01.332-.23.971.971 0 01.406-.085z"/><path id="let" fill="#1C85B5" d="M2.495 117.06H.465v-.978h3.233v8.668h2.044v.991H.24v-.991h2.256v-7.69zm11.156 4.887c0 .169-.002.31-.006.424a6.37 6.37 0 01-.021.321h-4.82c0 .702.197 1.241.589 1.617.392.376.957.564 1.695.564.2 0 .4-.008.601-.024.201-.016.395-.037.582-.065.186-.027.365-.058.536-.092.171-.034.33-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.194-1.494c0-.483.069-.94.208-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.271.225.37.15.68.364.933.64.253.275.445.61.575 1.005.13.394.194.835.194 1.322zm-1.237-.17a2.593 2.593 0 00-.089-.838 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.571-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.71.15-.215.1-.4.242-.555.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.177.84h3.61zm8.668 3.869a5.793 5.793 0 01-.834.146 8.52 8.52 0 01-.875.045c-.861 0-1.504-.195-1.928-.585-.424-.39-.635-.987-.635-1.794v-3.582h-1.921v-.998h1.92v-1.887l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.131.86.393 1.104.262.244.648.366 1.159.366.219 0 .458-.017.718-.051.26-.034.53-.088.813-.16v1.025z"/><path id="messages" fill="#181717" d="M35.84 125.741v-4.928c0-.215-.007-.39-.023-.527a1.2 1.2 0 00-.075-.325.348.348 0 00-.13-.167.43.43 0 00-.448.034c-.077.055-.16.144-.25.267a4.216 4.216 0 00-.293.488c-.107.203-.234.453-.38.749v4.41h-1.087v-4.8c0-.25-.008-.453-.024-.608a1.42 1.42 0 00-.075-.362.34.34 0 00-.133-.178.431.431 0 00-.43.02.988.988 0 00-.243.247c-.09.118-.188.28-.298.485-.11.205-.24.467-.39.786v4.41h-1.093v-6.864h.909l.055 1.306c.118-.26.233-.481.345-.663.112-.183.227-.33.345-.441.119-.112.245-.194.38-.247.134-.052.283-.078.447-.078.37 0 .65.12.841.362.191.242.287.615.287 1.121.11-.237.217-.447.321-.632a2.31 2.31 0 01.339-.465c.12-.125.254-.22.4-.287.146-.066.314-.1.506-.1.86 0 1.292.664 1.292 1.99v4.997H35.84zm8.6-3.794c0 .169-.002.31-.006.424a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.2 0 .401-.008.602-.024.2-.016.394-.037.58-.065.187-.027.366-.058.537-.092.171-.034.33-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.194-1.494c0-.483.07-.94.208-1.37a3.44 3.44 0 01.609-1.135c.266-.326.593-.585.98-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.271.225.37.15.68.364.933.64.253.275.445.61.575 1.005.13.394.194.835.194 1.322zm-1.237-.17a2.593 2.593 0 00-.089-.838 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.794-.154c-.26 0-.497.05-.71.15-.215.1-.4.242-.555.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.177.84h3.61zm8.572 2.091a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.638 4.638 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.401.114.8.2 1.197.26.396.059.79.088 1.182.088.57 0 .991-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.859-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.45-.56 1.649 1.649 0 01-.165-.759 1.744 1.744 0 01.588-1.265c.214-.195.504-.358.868-.488.365-.13.82-.195 1.367-.195.27 0 .568.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.871-.065c-.297 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.136.197.266.29.13.094.311.187.544.28.232.094.535.196.909.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.373.54.075.2.112.428.112.683zm7.698 0a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.493.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.638 4.638 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.402.114.8.2 1.197.26.396.059.79.088 1.182.088.57 0 .992-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.452-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.504-.358.868-.488.365-.13.82-.195 1.368-.195.268 0 .567.015.895.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.871-.065c-.296 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.137.197.267.29.13.094.31.187.543.28.232.094.535.196.91.305.405.118.747.243 1.025.372.278.13.503.275.676.435.173.159.298.339.373.54.075.2.113.428.113.683zm6.672 1.873l-.028-.923c-.374.37-.753.636-1.138.8-.385.164-.79.246-1.213.246-.392 0-.727-.05-1.005-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.397-.618 2.289 2.289 0 01-.126-.773c0-.688.256-1.227.769-1.617.513-.39 1.27-.584 2.273-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.19-.365-.387 0-.768.043-1.144.13-.376.086-.765.21-1.166.369v-1.074a7.36 7.36 0 011.087-.3 7.6 7.6 0 01.642-.1c.224-.025.45-.037.677-.037.415 0 .789.045 1.121.137.333.09.614.23.845.417.23.186.406.421.53.704.122.282.184.615.184.998v4.73h-1.066zm-.13-3.124h-1.511c-.296 0-.552.03-.766.09-.214.058-.39.143-.526.252-.137.11-.238.24-.304.393-.066.153-.1.325-.1.516 0 .132.021.259.062.38.041.12.107.227.198.32.092.094.21.168.356.223.146.055.323.082.533.082.273 0 .587-.083.94-.25.353-.166.726-.429 1.118-.789v-1.217zm8.36-2.768c.128.16.227.345.297.557.071.212.106.44.106.687 0 .355-.065.68-.195.974-.13.294-.313.546-.55.755a2.54 2.54 0 01-.85.489c-.331.116-.695.174-1.091.174-.287 0-.556-.03-.807-.092a2.064 2.064 0 01-.595-.229c-.086.128-.16.248-.218.362a.817.817 0 00-.09.383c0 .173.084.317.25.43.167.115.387.176.66.185l1.805.069c.341.009.657.052.946.13.29.077.538.189.746.335.207.145.369.325.485.54.116.214.174.462.174.745 0 .305-.066.594-.198.868a1.985 1.985 0 01-.612.721c-.276.207-.625.373-1.05.496-.423.123-.927.184-1.51.184-.556 0-1.029-.044-1.418-.133-.39-.089-.71-.212-.96-.37a1.49 1.49 0 01-.548-.56 1.51 1.51 0 01-.17-.714c0-.333.077-.624.232-.875.155-.25.394-.492.718-.725a1.133 1.133 0 01-.52-.475 1.322 1.322 0 01-.164-.632c0-.296.07-.567.209-.814.139-.246.304-.478.495-.697a3.57 3.57 0 01-.229-.307 1.91 1.91 0 01-.28-.701 2.536 2.536 0 01-.038-.462c0-.355.065-.68.195-.974.13-.294.312-.545.547-.755.235-.21.517-.373.848-.489.33-.116.696-.174 1.097-.174.168 0 .33.011.485.034.155.023.292.052.41.089h2.489v.97h-1.101zm-4.129 6.883c0 .324.169.56.506.708.337.148.807.222 1.408.222.378 0 .696-.034.954-.102.257-.069.465-.159.622-.27.157-.112.27-.24.338-.383.069-.144.103-.29.103-.441 0-.278-.114-.483-.342-.615-.228-.133-.577-.21-1.046-.233l-1.791-.061c-.15.1-.275.198-.373.294a1.292 1.292 0 00-.229.29 1.127 1.127 0 00-.116.294 1.309 1.309 0 00-.034.297zm.362-5.612c0 .219.037.42.11.602.073.182.175.337.307.465.133.127.289.226.469.297.18.07.379.106.598.106.237 0 .448-.04.632-.12.185-.08.34-.188.465-.324.125-.137.221-.294.287-.472.066-.178.1-.362.1-.554 0-.219-.037-.42-.11-.601a1.316 1.316 0 00-.308-.465 1.404 1.404 0 00-.468-.298c-.18-.07-.38-.106-.598-.106-.237 0-.448.041-.633.123-.184.082-.339.19-.464.325-.126.135-.221.29-.287.468-.067.178-.1.363-.1.554zm12.319.827c0 .169-.003.31-.007.424a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.2 0 .401-.008.602-.024.2-.016.394-.037.58-.065.188-.027.367-.058.538-.092.17-.034.329-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.589 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.043-.694 2.857 2.857 0 01-.601-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.208-1.37a3.44 3.44 0 01.609-1.135c.266-.326.593-.585.98-.776a2.94 2.94 0 011.32-.287c.479 0 .902.075 1.272.225.369.15.68.364.933.64.253.275.444.61.574 1.005.13.394.195.835.195 1.322zm-1.238-.17a2.593 2.593 0 00-.088-.838 1.763 1.763 0 00-.339-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.794-.154c-.26 0-.496.05-.71.15-.215.1-.4.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.178.84h3.61zm8.573 2.091a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.599.259c-.212.068-.43.119-.652.15a4.638 4.638 0 01-.657.048 12.3 12.3 0 01-1.281-.061 8.646 8.646 0 01-1.145-.199v-1.093c.4.114.8.2 1.196.26.396.059.79.088 1.182.088.57 0 .992-.077 1.265-.232.274-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.452-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.504-.358.868-.488.365-.13.82-.195 1.368-.195.269 0 .567.015.895.044.328.03.67.081 1.026.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.137.197.267.29.13.094.31.187.543.28.232.094.536.196.91.305.405.118.747.243 1.025.372.278.13.503.275.676.435.174.159.298.339.373.54.075.2.113.428.113.683z"/><path id="=" fill="#DBAF88" d="M105.916 121.325h-5.879v-1.005h5.879v1.005zm0 2.38h-5.879v-1.006h5.879v1.005z"/><path id="await" fill="#1C85B5" d="M120.025 125.741l-.027-.923c-.374.37-.753.636-1.138.8-.385.164-.79.246-1.214.246-.391 0-.726-.05-1.004-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.397-.618 2.289 2.289 0 01-.126-.773c0-.688.256-1.227.769-1.617.512-.39 1.27-.584 2.273-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.19-.365-.387 0-.769.043-1.145.13-.376.086-.764.21-1.165.369v-1.074a7.36 7.36 0 011.087-.3 7.6 7.6 0 01.642-.1c.224-.025.45-.037.677-.037.415 0 .789.045 1.121.137.333.09.614.23.844.417.23.186.407.421.53.704.123.282.185.615.185.998v4.73h-1.067zm-.13-3.124h-1.51c-.296 0-.552.03-.766.09-.214.058-.39.143-.526.252-.137.11-.238.24-.304.393-.067.153-.1.325-.1.516 0 .132.021.259.062.38.041.12.107.227.198.32.091.094.21.168.356.223.146.055.323.082.533.082.273 0 .587-.083.94-.25.353-.166.726-.429 1.118-.789v-1.217zm9.776-3.74l-.998 6.864h-1.443l-.99-2.87-.199-.698-.226.738-.95 2.83h-1.401l-.991-6.863h1.162l.574 4.662.123 1.04.294-.91.998-3.083h.855l1.073 3.042.307.91.103-.965.533-4.696h1.176zm5.749 6.864l-.027-.923c-.374.37-.754.636-1.139.8-.385.164-.79.246-1.213.246-.392 0-.727-.05-1.005-.15a1.883 1.883 0 01-.687-.414 1.603 1.603 0 01-.396-.618 2.289 2.289 0 01-.127-.773c0-.688.257-1.227.77-1.617.512-.39 1.27-.584 2.272-.584h1.422v-.602c0-.405-.13-.73-.39-.974-.26-.243-.656-.365-1.19-.365-.386 0-.768.043-1.144.13-.376.086-.765.21-1.166.369v-1.074a7.36 7.36 0 011.087-.3 7.6 7.6 0 01.643-.1c.223-.025.449-.037.677-.037.414 0 .788.045 1.12.137.333.09.615.23.845.417.23.186.407.421.53.704.123.282.184.615.184.998v4.73h-1.066zm-.13-3.124h-1.51c-.297 0-.552.03-.766.09-.215.058-.39.143-.527.252-.136.11-.238.24-.304.393-.066.153-.099.325-.099.516 0 .132.02.259.062.38.04.12.107.227.198.32.09.094.21.168.355.223.146.055.324.082.533.082.274 0 .587-.083.94-.25.354-.166.726-.429 1.118-.789v-1.217zm5.756-2.755h-2.03v-.984h3.233v5.872h2.044v.991h-5.503v-.991h2.256v-4.888zm.417-3.862c.132 0 .255.024.369.072a.893.893 0 01.297.201.919.919 0 01.27.663.96.96 0 01-.27.663.893.893 0 01-.297.202.944.944 0 01-.37.072.944.944 0 01-.368-.072.893.893 0 01-.298-.201.96.96 0 01-.27-.663.919.919 0 01.27-.664.893.893 0 01.298-.201.944.944 0 01.369-.072zm10.473 9.646a5.793 5.793 0 01-.834.146 8.52 8.52 0 01-.875.045c-.862 0-1.504-.195-1.928-.585-.424-.39-.636-.987-.636-1.794v-3.582h-1.92v-.998h1.92v-1.887l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.13.86.393 1.104.262.244.648.366 1.158.366.219 0 .458-.017.718-.051.26-.034.531-.088.814-.16v1.025z"/><path id="response" fill="#181717" d="M162.08 118.878h1.087l.034 1.265c.406-.488.806-.841 1.2-1.06a2.436 2.436 0 011.193-.328c.71 0 1.25.23 1.616.69.367.46.537 1.144.51 2.051h-1.203c.013-.601-.074-1.038-.264-1.309-.189-.271-.466-.407-.83-.407-.16 0-.32.029-.482.086a1.947 1.947 0 00-.5.273 4.47 4.47 0 00-.543.482 8.898 8.898 0 00-.615.711v4.41h-1.203v-6.864zm13.214 3.07c0 .168-.002.31-.007.423a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.201 0 .402-.008.602-.024.2-.016.394-.037.581-.065.187-.027.366-.058.537-.092.17-.034.329-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.518-.24a2.597 2.597 0 01-1.042-.694 2.857 2.857 0 01-.602-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.209-1.37a3.44 3.44 0 01.608-1.135c.267-.326.594-.585.981-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.27.225.37.15.681.364.934.64.253.275.444.61.574 1.005.13.394.195.835.195 1.322zm-1.237-.172a2.593 2.593 0 00-.09-.837 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.793-.154c-.26 0-.497.05-.711.15-.215.1-.4.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.178.84h3.61zm8.572 2.092a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.638 4.638 0 01-.657.048 12.3 12.3 0 01-1.281-.061 8.646 8.646 0 01-1.145-.199v-1.093c.4.114.8.2 1.196.26.397.059.79.088 1.183.088.57 0 .99-.077 1.264-.232.274-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.235-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.981-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.587-1.265c.215-.195.504-.358.869-.488.364-.13.82-.195 1.367-.195.269 0 .567.015.895.044.328.03.67.081 1.026.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.546.023-.748.068a1.553 1.553 0 00-.493.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.048.1.137.197.267.29.13.094.31.187.543.28.233.094.536.196.91.305.405.118.747.243 1.025.372.278.13.503.275.677.435.173.159.297.339.372.54.075.2.113.428.113.683zm8.128-1.681c0 .61-.086 1.143-.257 1.6-.17.455-.406.833-.707 1.134a2.9 2.9 0 01-1.066.677c-.41.15-.855.225-1.333.225-.22 0-.437-.011-.653-.034a4.952 4.952 0 01-.66-.116v2.87h-1.19v-9.665h1.06l.075 1.148c.342-.469.707-.798 1.094-.987.387-.19.807-.284 1.258-.284.392 0 .736.082 1.032.246.296.164.545.395.745.694.2.298.351.658.451 1.08.1.421.15.892.15 1.412zm-1.217.054c0-.36-.026-.69-.079-.991a2.586 2.586 0 00-.25-.772 1.37 1.37 0 00-.437-.503 1.104 1.104 0 00-.635-.181c-.15 0-.303.024-.458.072a1.857 1.857 0 00-.482.239 3.59 3.59 0 00-.527.444 7 7 0 00-.59.687v3.33c.218.09.448.162.69.215.241.052.478.078.71.078.643 0 1.147-.217 1.511-.652.365-.436.547-1.09.547-1.966zm9.023.014a4.55 4.55 0 01-.225 1.466c-.15.445-.367.825-.65 1.142a2.913 2.913 0 01-1.032.738 3.447 3.447 0 01-1.38.263c-.493 0-.934-.076-1.323-.229a2.617 2.617 0 01-.992-.673 2.98 2.98 0 01-.622-1.1c-.143-.438-.215-.942-.215-1.511 0-.534.075-1.02.226-1.46.15-.44.366-.818.649-1.135a2.913 2.913 0 011.032-.738 3.447 3.447 0 011.381-.263c.492 0 .933.076 1.323.229.39.153.72.376.991.67.271.294.479.66.622 1.097.144.437.215.939.215 1.504zm-1.216.055c0-.424-.047-.795-.14-1.111a2.237 2.237 0 00-.4-.793 1.65 1.65 0 00-.633-.479 2.077 2.077 0 00-.83-.16c-.356 0-.66.07-.913.208-.253.14-.46.325-.622.557a2.4 2.4 0 00-.355.81 4.06 4.06 0 00-.113.968c0 .423.047.795.14 1.114.093.319.227.584.4.796.173.212.383.372.629.479.246.107.524.16.834.16.355 0 .66-.07.912-.208.253-.14.46-.325.622-.557a2.4 2.4 0 00.356-.81 4.09 4.09 0 00.113-.974zm2.94-3.432h1.059l.048 1.107c.2-.237.394-.434.58-.591.187-.157.37-.284.55-.38a2.06 2.06 0 01.551-.201c.187-.039.38-.058.581-.058.707 0 1.241.208 1.603.625.363.417.544 1.045.544 1.884v4.477h-1.19v-4.382c0-.537-.1-.935-.3-1.192-.201-.258-.5-.387-.896-.387-.146 0-.288.022-.427.065a1.557 1.557 0 00-.434.226c-.15.107-.314.252-.49.434-.175.182-.372.41-.59.683v4.553h-1.19v-6.863zm13.131 4.99a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.638 4.638 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.4.114.8.2 1.196.26.397.059.79.088 1.183.088.57 0 .99-.077 1.264-.232.274-.155.41-.376.41-.663a.812.812 0 00-.064-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.981-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.503-.358.868-.488.364-.13.82-.195 1.367-.195.269 0 .567.015.895.044.329.03.67.081 1.026.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.546.023-.748.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.083.346c0 .123.024.235.072.335.048.1.137.197.267.29.13.094.31.187.543.28.233.094.536.196.91.305.405.118.747.243 1.025.372.278.13.503.275.677.435.173.159.297.339.372.54.075.2.113.428.113.683zm8.06-1.92c0 .168-.003.31-.007.423a6.37 6.37 0 01-.02.321h-4.82c0 .702.196 1.241.588 1.617.392.376.957.564 1.695.564.2 0 .401-.008.602-.024.2-.016.394-.037.58-.065.188-.027.366-.058.537-.092.171-.034.33-.072.475-.113v.978a9.161 9.161 0 01-2.365.308c-.588 0-1.094-.08-1.517-.24a2.597 2.597 0 01-1.043-.694 2.857 2.857 0 01-.601-1.114 5.273 5.273 0 01-.195-1.494c0-.483.07-.94.208-1.37a3.44 3.44 0 01.609-1.135c.266-.326.593-.585.98-.776a2.94 2.94 0 011.32-.287c.478 0 .902.075 1.271.225.37.15.68.364.933.64.253.275.445.61.575 1.005.13.394.195.835.195 1.322zm-1.238-.172a2.593 2.593 0 00-.089-.837 1.763 1.763 0 00-.338-.653 1.576 1.576 0 00-.57-.427 1.916 1.916 0 00-.794-.154c-.26 0-.497.05-.71.15-.215.1-.4.242-.554.424a2.19 2.19 0 00-.376.657 3.026 3.026 0 00-.178.84h3.61z"/><path id="." fill="#7E7C7B" d="M226.078 123.588a1.113 1.113 0 01.803.335c.103.105.184.227.243.366.06.139.089.288.089.447 0 .155-.03.301-.089.438a1.148 1.148 0 01-.605.601c-.139.06-.286.09-.44.09-.16 0-.308-.03-.445-.09a1.148 1.148 0 01-.602-.601 1.088 1.088 0 01-.089-.438 1.178 1.178 0 01.332-.813 1.12 1.12 0 01.803-.335z"/><path id="json" fill="#181717" d="M235.724 118.878v6.74c0 .479-.069.904-.205 1.275a2.557 2.557 0 01-.588.94 2.485 2.485 0 01-.94.581c-.372.132-.79.198-1.255.198a5.24 5.24 0 01-.987-.092 4.354 4.354 0 01-.872-.256v-1.135a4.512 4.512 0 001.976.472c.52 0 .927-.164 1.223-.493.296-.328.445-.81.445-1.449v-5.797h-3.364v-.984h4.567zm-.718-2.878c.132 0 .255.024.369.072a.893.893 0 01.297.201.96.96 0 01.27.664.919.919 0 01-.27.663.893.893 0 01-.297.201.944.944 0 01-.37.072.944.944 0 01-.368-.072.893.893 0 01-.298-.201.919.919 0 01-.27-.663.96.96 0 01.27-.663.893.893 0 01.298-.202.944.944 0 01.369-.072zm9.201 7.868a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.638 4.638 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.4.114.8.2 1.196.26.397.059.79.088 1.183.088.57 0 .991-.077 1.264-.232.274-.155.41-.376.41-.663a.812.812 0 00-.064-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.981-.328 6.963 6.963 0 01-.858-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.451-.56 1.649 1.649 0 01-.164-.759 1.744 1.744 0 01.588-1.265c.214-.195.503-.358.868-.488.364-.13.82-.195 1.367-.195.269 0 .567.015.895.044.329.03.67.081 1.026.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.872-.065c-.296 0-.545.023-.748.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.023.235.071.335.048.1.137.197.267.29.13.094.31.187.543.28.233.094.536.196.91.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.372.54.075.2.113.428.113.683zm8.237-1.613a4.55 4.55 0 01-.225 1.466c-.15.445-.367.825-.65 1.142a2.913 2.913 0 01-1.032.738 3.447 3.447 0 01-1.38.263c-.493 0-.934-.076-1.324-.229a2.617 2.617 0 01-.99-.673 2.98 2.98 0 01-.623-1.1c-.143-.438-.215-.942-.215-1.511 0-.534.075-1.02.225-1.46.15-.44.367-.818.65-1.135a2.913 2.913 0 011.032-.738 3.447 3.447 0 011.381-.263c.492 0 .933.076 1.323.229.39.153.72.376.99.67.272.294.48.66.623 1.097.144.437.215.939.215 1.504zm-1.216.055c0-.424-.047-.795-.14-1.111a2.237 2.237 0 00-.4-.793 1.65 1.65 0 00-.633-.479 2.077 2.077 0 00-.83-.16c-.356 0-.66.07-.913.208-.253.14-.46.325-.622.557a2.4 2.4 0 00-.356.81 4.06 4.06 0 00-.112.968c0 .423.046.795.14 1.114.093.319.227.584.4.796.173.212.383.372.629.479.246.107.524.16.834.16.355 0 .66-.07.912-.208.253-.14.46-.325.622-.557a2.4 2.4 0 00.356-.81 4.09 4.09 0 00.113-.974zm2.939-3.432h1.06l.047 1.107c.2-.237.395-.434.581-.591.187-.157.37-.284.55-.38a2.06 2.06 0 01.551-.201c.187-.039.38-.058.581-.058.706 0 1.24.208 1.603.625.362.417.544 1.045.544 1.884v4.477h-1.19v-4.382c0-.537-.1-.935-.3-1.192-.201-.258-.5-.387-.896-.387-.146 0-.288.022-.427.065a1.557 1.557 0 00-.434.226c-.15.107-.314.252-.49.434-.175.182-.372.41-.59.683v4.553h-1.19v-6.863z"/><path id="();" fill="#7E7C7B" d="M265.973 128.612c-2.101-1.946-3.152-4.097-3.152-6.453 0-.551.056-1.102.168-1.65.111-.55.291-1.1.54-1.652a9.028 9.028 0 01.984-1.654c.408-.551.904-1.098 1.487-1.64l.69.704c-1.768 1.745-2.652 3.68-2.652 5.803 0 1.058.223 2.074.67 3.05.447.974 1.107 1.895 1.982 2.76l-.717.732zm4.99-13.05c2.1 1.946 3.151 4.111 3.151 6.495 0 .492-.05 1.002-.15 1.53-.1.53-.271 1.07-.513 1.624a9.296 9.296 0 01-.974 1.685c-.408.57-.922 1.142-1.541 1.716l-.69-.704c.888-.88 1.55-1.798 1.988-2.755.438-.957.657-1.96.657-3.007 0-2.17-.882-4.12-2.646-5.852l.718-.731zm6.911 11.58c.232.01.458-.01.677-.06a2.02 2.02 0 00.577-.223c.167-.098.3-.22.4-.366a.86.86 0 00.15-.499.95.95 0 00-.098-.465 2.238 2.238 0 00-.223-.328 2.438 2.438 0 01-.222-.321.91.91 0 01-.099-.458.915.915 0 01.222-.588.857.857 0 01.287-.212 1.12 1.12 0 01.875.017c.149.066.278.168.39.304.112.137.2.308.267.513.066.205.099.447.099.725 0 .378-.07.741-.209 1.09a2.657 2.657 0 01-.622.926c-.276.27-.62.483-1.032.643-.412.16-.892.24-1.439.24v-.937zm2.14-8.387c.14 0 .275.028.403.085a1.107 1.107 0 01.564.564.978.978 0 01.085.404.971.971 0 01-.085.406 1.12 1.12 0 01-.229.332 1.058 1.058 0 01-.738.308c-.146 0-.282-.028-.407-.083a1.07 1.07 0 01-.557-.557 1.006 1.006 0 01-.082-.406 1.058 1.058 0 01.308-.738 1.12 1.12 0 01.33-.23.971.971 0 01.408-.085z"/><path id="..." fill="#DBAF88" d="M2.857 137.588a1.113 1.113 0 01.804.335c.102.105.183.227.242.366.06.139.09.288.09.447 0 .155-.03.301-.09.438a1.148 1.148 0 01-.605.601c-.139.06-.286.09-.44.09-.16 0-.308-.03-.445-.09a1.148 1.148 0 01-.601-.601 1.088 1.088 0 01-.09-.438 1.178 1.178 0 01.332-.813 1.12 1.12 0 01.803-.335zm7.698 0a1.113 1.113 0 01.803.335c.102.105.183.227.243.366.059.139.088.288.088.447 0 .155-.03.301-.088.438a1.148 1.148 0 01-.605.601c-.14.06-.286.09-.441.09-.16 0-.308-.03-.445-.09a1.148 1.148 0 01-.601-.601 1.088 1.088 0 01-.09-.438 1.178 1.178 0 01.332-.813 1.12 1.12 0 01.804-.335zm7.697 0a1.113 1.113 0 01.803.335c.103.105.184.227.243.366.06.139.089.288.089.447 0 .155-.03.301-.09.438a1.148 1.148 0 01-.604.601c-.14.06-.286.09-.441.09-.16 0-.308-.03-.444-.09a1.148 1.148 0 01-.602-.601 1.088 1.088 0 01-.089-.438 1.178 1.178 0 01.332-.813 1.12 1.12 0 01.803-.335z"/><path id="</script>" fill="#7E7C7B" d="M5.339 171.099l-.745.752L0 168.016l4.594-3.835.745.758-3.726 3.063 3.726 3.097zm8.032-9.017l-4.69 11.129H7.548l4.69-11.129h1.134zm7.615 7.786a1.634 1.634 0 01-.458 1.159 2.084 2.084 0 01-.492.38 3.272 3.272 0 01-.598.259c-.212.068-.43.119-.653.15a4.638 4.638 0 01-.656.048 12.3 12.3 0 01-1.282-.061 8.646 8.646 0 01-1.145-.199v-1.093c.401.114.8.2 1.196.26.397.059.791.088 1.183.088.57 0 .991-.077 1.265-.232.273-.155.41-.376.41-.663a.812.812 0 00-.065-.332.753.753 0 00-.236-.28 2.254 2.254 0 00-.53-.277 11.81 11.81 0 00-.98-.328 6.963 6.963 0 01-.859-.311 2.877 2.877 0 01-.68-.414 1.776 1.776 0 01-.45-.56 1.649 1.649 0 01-.165-.759 1.744 1.744 0 01.588-1.265c.214-.195.503-.358.868-.488s.82-.195 1.367-.195c.27 0 .568.015.896.044.328.03.67.081 1.025.154v1.06a9.803 9.803 0 00-1.063-.202 6.926 6.926 0 00-.871-.065c-.297 0-.546.023-.749.068a1.553 1.553 0 00-.492.188.734.734 0 00-.27.28.748.748 0 00-.082.346c0 .123.024.235.072.335.047.1.136.197.266.29.13.094.311.187.544.28.232.094.535.196.909.305.405.118.747.243 1.025.372.278.13.504.275.677.435.173.159.297.339.373.54.075.2.112.428.112.683zm7.595 1.62c-.31.119-.628.206-.954.263a5.845 5.845 0 01-1.008.086c-1.085 0-1.92-.294-2.505-.882-.586-.588-.879-1.447-.879-2.577 0-.542.085-1.035.253-1.477a3.142 3.142 0 011.805-1.863c.424-.17.89-.256 1.401-.256.356 0 .688.025.998.075.31.05.607.133.89.247v1.134a3.771 3.771 0 00-.907-.338 4.248 4.248 0 00-.953-.106c-.306 0-.594.058-.865.174-.271.116-.51.284-.714.503a2.38 2.38 0 00-.486.8c-.118.314-.178.67-.178 1.066 0 .83.202 1.45.605 1.863.404.412.963.618 1.679.618a3.897 3.897 0 001.818-.438v1.108zm2.646-6.61h1.086l.035 1.265c.405-.488.805-.841 1.2-1.06a2.436 2.436 0 011.192-.328c.711 0 1.25.23 1.617.69.367.46.537 1.144.51 2.051h-1.204c.014-.601-.074-1.038-.263-1.309-.19-.271-.466-.407-.83-.407-.16 0-.32.029-.483.086a1.947 1.947 0 00-.499.273 4.47 4.47 0 00-.543.482 8.898 8.898 0 00-.615.711v4.41h-1.203v-6.864zm9.754.984h-2.03v-.984h3.234v5.872h2.044v.991h-5.503v-.991h2.255v-4.888zm.417-3.862c.133 0 .256.024.37.072a.893.893 0 01.297.201.919.919 0 01.27.663.96.96 0 01-.27.663.893.893 0 01-.297.202.944.944 0 01-.37.072.944.944 0 01-.369-.072.893.893 0 01-.297-.201.96.96 0 01-.27-.663.919.919 0 01.27-.664.893.893 0 01.297-.201.944.944 0 01.37-.072zm10.808 6.187c0 .61-.085 1.143-.256 1.6-.171.455-.407.833-.708 1.134a2.9 2.9 0 01-1.066.677c-.41.15-.855.225-1.333.225-.219 0-.437-.011-.653-.034a4.952 4.952 0 01-.66-.116v2.87h-1.19v-9.665h1.06l.076 1.148c.341-.469.706-.798 1.093-.987.388-.19.807-.284 1.258-.284.392 0 .736.082 1.032.246.297.164.545.395.745.694.201.298.351.658.452 1.08.1.421.15.892.15 1.412zm-1.217.054c0-.36-.026-.69-.078-.991a2.586 2.586 0 00-.25-.772 1.37 1.37 0 00-.437-.503 1.104 1.104 0 00-.636-.181c-.15 0-.303.024-.458.072a1.857 1.857 0 00-.482.239 3.59 3.59 0 00-.526.444 7 7 0 00-.592.687v3.33c.219.09.45.162.69.215.242.052.48.078.712.078.642 0 1.146-.217 1.51-.652.365-.436.547-1.09.547-1.966zm8.58 3.405a5.793 5.793 0 01-.835.146 8.52 8.52 0 01-.875.045c-.861 0-1.504-.195-1.927-.585-.424-.39-.636-.987-.636-1.794v-3.582h-1.921v-.998h1.92v-1.887l1.19-.307v2.194h3.083v.998h-3.083v3.486c0 .492.131.86.393 1.104.262.244.649.366 1.16.366.218 0 .457-.017.717-.051.26-.034.53-.088.813-.16v1.025zm2.501-6.707l.739-.758 4.593 3.835-4.593 3.835-.739-.752 3.726-3.07-3.726-3.09z"/></g><text id="evil.com" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="16" font-weight="bold"><tspan x="129" y="36">evil.com</tspan></text><path id="Rectangle-1-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M494 48h140v199H494z"/><text id="got-the-cookie?-okay" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x="508.465" y="133">got the cookie?</tspan> <tspan x="544.852" y="155">okay!</tspan></text><text id="gmail.com" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="16" font-weight="bold"><tspan x="528" y="36">gmail.com</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M308.215 100.812l1.487.202 158.667 21.513 1.075-7.927 17.551 11.967-20.104 6.861 1.075-7.928-158.668-21.514-1.486-.201.403-2.973z"/><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M491.796 151.82l.383 2.976-1.488.192L332.54 175.34l1.021 7.935-20.057-6.997 17.632-11.847 1.021 7.934 158.152-20.354 1.487-.191z"/><text id="GET-/messages" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal" transform="rotate(7 408.585 101.617)"><tspan x="352.085" y="107.617">GET /messages</tspan></text><text id="cookie:-user=John" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal" transform="rotate(7 402.573 127.8)"><tspan x="336.073" y="133.8">cookie: user=John</tspan></text><text id="{"messages":-[...]}" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal" transform="rotate(-7 408.333 176.276)"><tspan x="342.833" y="182.276">{"messages": [...]}</tspan></text></g></g></svg> \ No newline at end of file diff --git a/5-network/05-fetch-crossorigin/demo.view/index.html b/5-network/05-fetch-crossorigin/demo.view/index.html new file mode 100644 index 0000000000..7e367598cc --- /dev/null +++ b/5-network/05-fetch-crossorigin/demo.view/index.html @@ -0,0 +1,31 @@ +<!doctype html> +<script> + +async function init() { + const response = await fetch('long.txt'); + const reader = response.body.getReader(); + + const contentLength = +response.headers.get('Content-Length'); + let receivedLength = 0; + + while(true) { + const chunk = await reader.read(); + + if (chunk.done) { + console.log("done!"); + break; + } + + receivedLength += chunk.value.length; + console.log(`${receivedLength}/${contentLength} received`) + } + + let result = await response.text(); + console.log(result); + //const chunkCount = await read(reader); + //console.log(`Finished! Received ${chunkCount} chunks.`); +} + +init(); + +</script> diff --git a/5-network/05-fetch-crossorigin/demo.view/server.js b/5-network/05-fetch-crossorigin/demo.view/server.js new file mode 100644 index 0000000000..588ca0653e --- /dev/null +++ b/5-network/05-fetch-crossorigin/demo.view/server.js @@ -0,0 +1,31 @@ +const Koa = require('koa'); +const app = new Koa(); + +const Router = require('koa-router'); + +let router = new Router(); + +router.get('/script', async (ctx) => { + let callback = ctx.query.callback; + + if (!callback) { + ctx.throw(400, 'Callback required!'); + } + + ctx.type = 'application/javascript'; + ctx.body = `${callback}({ + temperature: 25, + humidity: 78 + })`; +}); + +app + .use(router.routes()) + .use(router.allowedMethods()); + + +if (!module.parent) { + http.createServer(app.callback()).listen(8080); +} else { + exports.accept = app.callback(); +} diff --git a/5-network/05-fetch-crossorigin/xhr-another-domain.svg b/5-network/05-fetch-crossorigin/xhr-another-domain.svg new file mode 100644 index 0000000000..2ed70febde --- /dev/null +++ b/5-network/05-fetch-crossorigin/xhr-another-domain.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="633" height="411" viewBox="0 0 633 411"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="network" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="xhr-another-domain.svg"><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M1 16h128v64H1z"/><text id="JavaScript" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="35" y="50">JavaScript</tspan></text><path id="Rectangle-228" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M261 16h128v64H261z"/><text id="Browser" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="296" y="50">Browser</tspan></text><path id="Rectangle-229" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M504 16h128v64H504z"/><text id="Server" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="544" y="50">Server</tspan></text><path id="Line" stroke="#7E7C7B" stroke-linecap="square" d="M66 81v320M326 81v320M569 81v320"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M312 133l14 7-14 7v-6H66v-2h246v-6z"/><text id="Origin:-https://java" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="354.313" y="205">Origin: https://javascript.info</tspan></text><text id="HTTP-request" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="401.015" y="174">HTTP-request</tspan></text><text id="fetch()" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="171" y="131">fetch()</tspan></text><text id="HTTP-response" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="398.419" y="250">HTTP-response</tspan></text><text id="Access-Control-Allow" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="347" y="287">Access-Control-Allow-Origin: * </tspan> <tspan x="364.141" y="305">(or https://javascript.info)</tspan></text><text id="if-the-header-allows" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="85" y="325">if the header allows, then success,</tspan></text><text id="otherwise-fail" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="148" y="353">otherwise fail</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M554 180l14 7-14 7v-6H328v-2h226v-6z"/><path id="Line-2" fill="#C06334" fill-rule="nonzero" d="M340 258v6h228v2H340v6l-14-7 14-7z"/><path id="Line-3" fill="#C06334" fill-rule="nonzero" d="M80 326v6h247v2H80v6l-14-7 14-7z"/></g></g></svg> \ No newline at end of file diff --git a/5-network/05-fetch-crossorigin/xhr-preflight.svg b/5-network/05-fetch-crossorigin/xhr-preflight.svg new file mode 100644 index 0000000000..c96e05ab28 --- /dev/null +++ b/5-network/05-fetch-crossorigin/xhr-preflight.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="620" height="633" viewBox="0 0 620 633"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="network" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="xhr-preflight.svg"><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M1 16h128v64H1z"/><text id="JavaScript" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="33" y="50">JavaScript</tspan></text><path id="Rectangle-228" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M240 16h128v64H240z"/><text id="Browser" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="278" y="48">Browser</tspan></text><path id="Rectangle-229" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M491 16h128v64H491z"/><text id="Server" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="534" y="50">Server</tspan></text><path id="Line" stroke="#7E7C7B" stroke-linecap="square" d="M66 82v520M305 82v520M556 82v520"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M291 133l14 7-14 7-.001-6H67v-2h223.999l.001-6z"/><text id="fetch()" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="166" y="131">fetch()</tspan></text><text id="OPTIONS" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="400" y="173">OPTIONS</tspan></text><text id="Origin-Access-Contro" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="406.721" y="198">Origin</tspan> <tspan x="321.866" y="216">Access-Control-Request-Method</tspan> <tspan x="320" y="234">Access-Control-Request-Headers</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M319 285.5v6h237v2H319v6l-14-7 14-7z"/><text id="200-OK" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="407" y="285">200 OK</tspan></text><path id="Line-4" fill="#C06334" fill-rule="nonzero" d="M542 173l14 7-14 7-.001-6H305v-2h236.999l.001-6z"/><path id="Line-2" fill="#C06334" fill-rule="nonzero" d="M319 495v6h237v2H319v6l-14-7 14-7z"/><path id="Line-3" fill="#C06334" fill-rule="nonzero" d="M90.5 544.5v8h214v3h-214v8l-19-9.5 19-9.5z"/><text id="Access-Control-Allow" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="336" y="522">Access-Control-Allow-Origin</tspan></text><text id="Main-HTTP-response" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="355" y="495">Main HTTP-response</tspan></text><text id="otherwise-error" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="130" y="570">otherwise error</tspan></text><text id="if-allowed:-success," fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="115.5" y="545">if allowed: success,</tspan></text><path id="Line-5" fill="#C06334" fill-rule="nonzero" d="M542 429l14 7-14 7-.001-6H304v-2h237.999l.001-6z"/><text id="Origin" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="405" y="454">Origin</tspan></text><text id="Main-HTTP-request" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="360" y="429">Main HTTP-request</tspan></text><text id="preflight" fill="#7E7C7B" font-family="OpenSans-Regular, Open Sans" font-size="22" font-weight="normal"><tspan x="210.145" y="186">preflight</tspan></text><text id="if-allowed" fill="#7E7C7B" font-family="OpenSans-Regular, Open Sans" font-size="22" font-weight="normal"><tspan x="198.338" y="441">if allowed</tspan></text><text id="1" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="570.139" y="186">1</tspan></text><text id="2" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="570.139" y="302">2</tspan></text><text id="3" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="570.139" y="443">3</tspan></text><text id="4" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="24" font-weight="normal"><tspan x="570.139" y="509">4</tspan></text><text id="Access-Control-Allow" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"> <tspan x="340.917" y="308.33">Access-Control-Allow-Origin</tspan> <tspan x="335.366" y="326.33">Access-Control-Allow-Method</tspan> <tspan x="333.5" y="344.33">Access-Control-Allow-Headers</tspan> <tspan x="352.47" y="362.33">Access-Control-Max-Age</tspan></text></g></g></svg> \ No newline at end of file diff --git a/5-network/06-fetch-api/article.md b/5-network/06-fetch-api/article.md new file mode 100644 index 0000000000..d337c365e4 --- /dev/null +++ b/5-network/06-fetch-api/article.md @@ -0,0 +1,186 @@ + +# Fetch API + +これまでの記事で、私たちは fetch について多くのことを知っています。 + +それでは、そのすべての機能をカバーするために、API の残りの部分を見ていきましょう。 + +以下は、指定可能なすべての fetch オプションとそのデフォルト値(コメントは他の選択肢です)のリストです。: + +```js +let promise = fetch(url, { + method: "GET", // POST, PUT, DELETE, etc. + headers: { + "Content-Type": "text/plain;charset=UTF-8" // 文字列の本文の場合, 本文に依存します + }, + body: undefined // 文字列, FormData, Blob, BufferSource, あるいは URLSearchParams + referrer: "about:client", // no-referrer の場合は "", あるいは現在のオリジンの URL + referrerPolicy: "no-referrer-when-downgrade", // no-referrer, origin, same-origin... + mode: "cors", // same-origin, no-cors + credentials: "same-origin", // omit, include + cache: "default", // no-store, reload, no-cache, force-cache, or only-if-cached + redirect: "follow", // manual, error + integrity: "", // "sha256-abcdef1234567890" のようなハッシュ + keepalive: false, // true + signal: undefined, // リクエストを中止するための AbortController + window: window // null +}); +``` + +チャプター <info:fetch-basics> で `method`, `headers` そして `body` を詳しく説明しました。 + +`signal` オプションは <info:fetch-abort> で説明しています。 + +それでは、オプションの残りを調べてみましょう。 + +## referrer, referrerPolicy + +これらのオプションは `fetch` がどのように HTTP `Referer` へヘッダを設定するかを管理します。 + +そのヘッダには、リクエストを行ったページの URL が含まれます。ほとんどの場合、これは非常に小さな情報の役割を果たしますが、セキュリティ目的で削除または変更するのが理にかなっていることがあります。 + +**`referrer` オプションにより、現在のオリジン内の任意の `Referer` を設定するか、それを無効にすることができます。** + +referer を送らないようにするには、空文字をセットします: +```js +fetch('/page', { +*!* + referrer: "" // Referer ヘッダなし +*/!* +}); +``` + +現在のオリジン内の別の URL を設定するには次のようにします: + +```js +fetch('/page', { + // https://javascript.info にいる想定です + // 任意の Referer ヘッダが設定できますが、現在のオリジン内のみです +*!* + referrer: "https://javascript.info/anotherpage" +*/!* +}); +``` + +**`referrerPolicy` オプションは `Referer` に対する一般的なルールを設定します。** + +指定可能な値は[Referrer Policy specification](https://w3c.github.io/webappsec-referrer-policy/)で説明されています: + +- **`"no-referrer-when-downgrade"`** -- デフォルト値: HTTPS から HTTP (より安全性の低いプロトコル)にリクエストを送信する場合以外は、常に `Referer` は送信されます。 +- **`"no-referrer"`** -- けして `Referer` を送信しません。 +- **`"origin"`** -- `Referer` には完全なページURLではなく、オリジンのみを送信します。例えば、`http://site.com/path` の代わりに `http://site.com`。 +- **`"origin-when-cross-origin"`** -- 同じオリジンには完全な referrer を送信しますが、クロスオリジンリクエストの場合はオリジン部分のみを送信します。 +- **`"same-origin"`** -- 同じオリジンには完全な referrer を送信しますが、クロスオリジンリクエストの場合は referrer を送信しません。 +- **`"strict-origin"`** -- オリジンのみを送信し、HTTPS→HTTP のリクエストの場合には referrer を送信しません。 +- **`"strict-origin-when-cross-origin"`** -- 同じオリジンの場合は完全な referrer を送信し、クロスオリジンの場合はHTTPS→HTTP リクエストでなければオリジンのみを送信、 HTTPS→HTTP の場合は何も送信しません。 + +外部からは見えるべきでないURL構造をもつ管理者の区域があるとします。 + +もしクロスオリジンの `fetch` を送信した場合、デフォルトでは自身のページの完全なURLを持つ `Referer` ヘッダを送信します(HTTPS から HTTP へリクエストする場合を除く。この場合は `Referer` はありません)。 + +E.g. `Referer: https://javascript.info/admin/secret/paths`. + +referrer を完全に隠したい場合: + +```js +fetch('https://another.com/page', { + referrerPolicy: "no-referrer" // Referer なし。 referrer: "" と同じです。 +}); +``` + +そうではなく、リモート側でリクエストがどこから来たのかを確認したい場合には、URLの "オリジン" 部分だけを送ることができます。: + +```js +fetch('https://another.com/page', { + referrerPolicy: "strict-origin" // Referer: https://javascript.info +}); +``` + +## mode + +`mode` オプションはクロスオリジンリクエストを防ぐ安全装置として機能します。: + +- **`"cors"`** -- デフォルト。<info:fetch-crossorigin> で説明されているように、クロスオリジンリクエストは許可されます。, +- **`"same-origin"`** -- クロスオリジンリクエストは禁止されています, +- **`"no-cors"`** -- 単純なクロスオリジンリクエストのみ許可されています。 + +これは、fetch URL がサードパーティから来たもので、クロスオリジンの機能を制限するために "機能をオフ" にしたい場合に役立ちます。 + +## credentials + +`credentials` オプションは、`fetch` がリクエストと一緒に cookie と HTTP-Authorization ヘッダを送るべきかを指定するものです。 + +- **`"same-origin"`** -- デフォルトです。クロスオリジンリクエストに対しては送信しません。 +- **`"include"`** -- 常に送信し、クロスオリジンのサーバから `Accept-Control-Allow-Credentials` を要求します, +- **`"omit"`** -- 同一オリジンのリクエストの場合でも送信しません。 + +## cache + +デフォルトでは、`fetch` リクエストは標準の HTTP キャッシングを使用します。つまり、`Expires` や `Cache-Control` ヘッダに従ったり、`If-Modified-Since` を送信したりします。通常のHTTPリクエストがするのと同じようにします。 + +`cache` オプションを使うことで、HTTP キャッシュを無視あるいはその使い方を調整することができます。: + +- **`"default"`** -- `fetch` は標準の HTTP キャッシュのルールとヘッダを使用します; +- **`"no-store"`** -- HTTP キャッシュを完全に無視します。 `If-Modified-Since`, `If-None-Match`, `If-Unmodified-Since`, `If-Match`, または `If-Range` のヘッダを設定している場合は、このモードがデフォルトになります; +- **`"reload"`** -- (たとえキャッシュされていたとしても)HTTP キャッシュから結果を取りませんが、キャッシュにレスポンスを埋めます(レスポンスヘッダが許可している場合)。 +- **`"no-cache"`** -- キャッシュされているレスポンスがある場合は条件付きリクエストを作成し、それ以外の場合は通常のリクエストを作成します。レスポンスで HTTP キャッシュを埋めます。 +- **`"force-cache"`** -- たとえ古くてもHTTP キャッシュからのレスポンスを使用します。HTTPキャッシュにレスポンスがない場合は、通常の HTTP リクエストを行い通常通りの振る舞いをします; +- **`"only-if-cached"`** -- たとえ古くてもHTTP キャッシュからのレスポンスを使用します。HTTPキャッシュにレスポンスがない場合、エラーになります。 `mode` が `"same-origin"` の場合にのみ動作します。 + +## redirect + +通常、 `fetch` は 301 や 302 などのように、HTTP リダイレクトに透過的に従います。 + +`redirect` オプションでそれを変更することができます: + +- **`"follow"`** -- デフォルト。HTTP リダイレクトに従います, +- **`"error"`** -- HTTP リダイレクトの場合にはエラーになります。, +- **`"manual"`** -- HTTP リダイレクトには従いませんが、`response.url` が新しい URL になり、`response.redirected` が `true` になります。必要に応じて新しい URL へ手動でリダイレクトすることができます。 + +## integrity + +`integrity` オプションは、レスポンスが既知のチェックサムに一致するかを確認することができます。 + +[仕様](https://w3c.github.io/webappsec-subresource-integrity/) で説明されている通り、サポートされているハッシュ関数は SHA-256, SHA-384, と SHA-512 であり、ブラウザによっては他のものがあるかもしれません。 + +例えば、ファイルをダウンロードし、その SHA-256 のチェックサムが "abc" とします(実際のチェックサムはもちろんもっと長いです)。 + +これを次のようにして、`integrity` オプションに指定することができます: + +```js +fetch('http://site.com/file', { + integrity: 'sha256-abc' +}); +``` + +すると、`fetch` は自身で SHA-256 を計算し、文字列比較をします。一致しない場合はエラーが発生します。 + +## keepalive + +`keepalive` オプションは、リクエストがページよりも長生きする可能性があること意味します。 + +例えば、ユーザ体験を向上させるために、現在の訪問者がどのように我々のページを使用しているか(マウスクリック、閲覧したページの断片 など)に関する統計を収集するとします。 + +訪問者がページを離れるとき -- それをサーバ上に保存したいです。 + +`window.onunload` を使用すると実現できます: + +```js run +window.onunload = function() { + fetch('/analytics', { + method: 'POST', + body: "statistics", +*!* + keepalive: true +*/!* + }); +}; +``` + +通常、ドキュメントがアンロードされると、関連するすべてのネットワークリクエストは中止されます。しかし `keepalive` オプションは、たとえページから離れた後だとしてもバックグランドでそれを実行するようブラウザに指示します。そのため、リクエストが成功するためにはこれは不可欠です。 + +- メガバイトを送信することはできません: keepalive リクエストの場合の本文の制限は 64kb です。 + - もしもより多くのデータを集める場合は、定期的にそれを送ります。そうすれば、"onunload" 時のリクエストは多くはならないでしょう。 + - この制限は、現在進行中のすべてのリクエストに対するものです。なので、それぞれ 64kb のリクエストを 100 個作成することで騙すことができます。 +- リクエストが `onunload` で行われた場合、サーバの応答は得られません。なぜならドキュメントはその時点ですでにアンロードされているからです。 + - 通常、サーバはこのようなリクエストに対しては空のレスポンスを返すので問題はありません。 diff --git a/5-network/06-fetch-api/logo-fetch.svg b/5-network/06-fetch-api/logo-fetch.svg new file mode 100644 index 0000000000..5a58b209b5 --- /dev/null +++ b/5-network/06-fetch-api/logo-fetch.svg @@ -0,0 +1,4 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> +<circle cx="50" cy="50" r="45" fill="#fff" stroke="#3c790a" stroke-width="10"/> +<path d="m34,55a60,60,0,0,0,20,-20a6,10,0,0,1,13,-1a10,6,0,0,1,-1,13a60,60,0,0,0,-20,20a6,10,0,0,1,-13,1a10,6,0,0,1,1,-13" fill="#3c790a"/> +</svg> diff --git a/5-network/06-fetch-api/post.view/index.html b/5-network/06-fetch-api/post.view/index.html new file mode 100644 index 0000000000..ba7f76065e --- /dev/null +++ b/5-network/06-fetch-api/post.view/index.html @@ -0,0 +1,36 @@ +<!doctype html> +<script> +(async () { + + const response = await fetch('long.txt'); + const reader = response.body.getReader(); + + const contentLength = +response.headers.get('Content-Length'); + let receivedLength = 0; + let chunks = []; + while(true) { + const chunk = await reader.read(); + + if (chunk.done) { + console.log("done!"); + break; + } + + chunks.push(chunk.value); + receivedLength += chunk.value.length; + console.log(`${receivedLength}/${contentLength} received`) + } + + + let chunksMerged = new Uint8Array(receivedLength); + let length = 0; + for(let chunk of chunks) { + chunksMerged.set(chunk, length); + length += chunk.length; + } + + let result = new TextDecoder("utf-8").decode(chunksMerged); + console.log(result); +})(); + +</script> diff --git a/5-network/06-fetch-api/post.view/server.js b/5-network/06-fetch-api/post.view/server.js new file mode 100644 index 0000000000..5e182a2ba7 --- /dev/null +++ b/5-network/06-fetch-api/post.view/server.js @@ -0,0 +1,55 @@ +let http = require('http'); +let url = require('url'); +let querystring = require('querystring'); +let static = require('node-static'); +let file = new static.Server('.', { + cache: 0 +}); + + +function accept(req, res) { + + if (req.method == 'POST') { + let chunks = []; + let length = 0; + + req.on('data', function (data) { + chunks.push(data); + length += data.length; + + // Too much POST data, kill the connection! + if (length > 1e6) { + request.connection.destroy(); + } + }); + + req.on('end', function() { + // let post = JSON.parse(chunks.join('')); + + if (req.url == '/user') { + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ message: 'User saved' })); + } else if (req.url == '/image') { + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ message: "Image saved", imageSize: length })); + } else { + res.writeHead(404); + res.end("Not found"); + } + }); + + + } else { + file.serve(req, res); + } + +} + + +// ------ запустить сервер ------- + +if (!module.parent) { + http.createServer(accept).listen(8080); +} else { + exports.accept = accept; +} diff --git a/5-network/07-url/article.md b/5-network/07-url/article.md new file mode 100644 index 0000000000..461ff8533f --- /dev/null +++ b/5-network/07-url/article.md @@ -0,0 +1,96 @@ + +# URL オブジェクト + +組み込みの [URL](https://url.spec.whatwg.org/#api) クラスは URL の作成や解析に関する便利なインタフェースを提供します。 + +なお、必ずしもこれを使う必要はありません。厳密に `URL` オブジェクトを必要とするネットワークメソッドはなく、文字列でも十分です。しかし非常に役立つときもあります。 + +## URL を作成する + +新しい URL オブジェクトを作成する構文です: + +```js +new URL(url, [base]) +``` + +- **`url`** -- テキスト url です +- **`base`** -- `url` のオプションのベースです + +`URL` オブジェクトを使うと、すぐにその構成要素にアクセスできます。そのため、これは url を解析する良い方法です。e.g: + +```js run +let url = new URL('https://javascript.info/url'); + +alert(url.protocol); // https: +alert(url.host); // javascript.info +alert(url.pathname); // /url +``` + +チートシートです: + +![](url-object.svg) + +- `href` は完全な url, `url.toString()` と同じです。 +- `protocol` はコロン `:` で終わります。 +- `search` ははてな `?` から始まります。 +- `hash` はハッシュ `#` から始まります。 +- HTTP 認証がある場合、`user` と `password` プロパティもあります。 + +また、相対 url を作成するときにも利用できます。この場合は2つ目の引数を使います: + +```js run +let url = new URL('profile/admin', 'https://javascript.info'); + +alert(url); // https://javascript.info/profile/admin + +url = new URL('tester', url); // 現在の url パスに基準にして `tester` に移動します + +alert(url); // https://javascript.info/profile/tester +``` + +```smart header="文字列の代わりにどこでも `URL` を使うことができます" +`URL` オブジェクト は `fetch` や `XMLHttpRequest`, 文字列の url が期待されているところならどこでも使うことができます。 + +ほぼ大多数のメソッドは、自動的に文字列に変換します。 +``` + +## SearchParams + +与えられた検索パラメータで url を作成したいとしましょう、例えば `https://google.com/search?query=value` です。 + +これらは正しくエンコードされている必要があります。 + +非常に古いブラウザでは、`URL` が登場する以前、組み込み関数 `encodeURIComponent/decodeURIComponent` を使っていました。 + +いま、それらは必要ありません: `url.searchParams` は[URLSearchParams](https://url.spec.whatwg.org/#urlsearchparams)型のオブジェクトです。 + +これは検索パラメータに対して便利なメソッドを提供しています。: + +- **`append(name, value)`** -- パラメータを追加します, +- **`delete(name)`** -- パラメータを削除します, +- **`get(name)`** -- パラメータを取得します, +- **`getAll(name)`** -- 指定した name のすべてのパラメータを取得します(e.g. `?user=John&user=Pete` など複数ある場合), +- **`has(name)`** -- パラメータが存在するかをチェックします, +- **`set(name, value)`** -- パラメータを設定/置き換えます, +- **`sort()`** -- name でパラメータをソートします。めったに必要とされません, +- ...また、`Map` のように反復可能です. + +そのため、`URL` オブジェクトは url パラメータを操作するための簡単な方法も提供します。 + +例: + +```js run +let url = new URL('https://google.com/search'); +url.searchParams.set('query', 'test me!'); + +alert(url); // https://google.com/search?query=test+me%21 + +url.searchParams.set('tbs', 'qdr:y'); // add param for date range: past year + +alert(url); // https://google.com/search?query=test+me%21&tbs=qdr%3Ay + +// iterate over search parameters (decoded) +for(let [name, value] of url.searchParams) { + alert(`${name}=${value}`); // query=test me!, then tbs=qdr:y +} +``` diff --git a/5-network/07-url/url-object.svg b/5-network/07-url/url-object.svg new file mode 100644 index 0000000000..628ccc13be --- /dev/null +++ b/5-network/07-url/url-object.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="698" height="246" viewBox="0 0 698 246"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="network" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="url-object.svg"><path id="Line-Copy" fill="#C06334" stroke="#C06334" stroke-dasharray="1" stroke-linecap="square" stroke-width="2" d="M27 38v180"/><path id="Line-Copy-2" fill="#C06334" stroke="#C06334" stroke-dasharray="1" stroke-linecap="square" stroke-width="2" d="M109 132v86"/><path id="Line-Copy-6" fill="#C06334" stroke="#C06334" stroke-dasharray="1" stroke-linecap="square" stroke-width="2" d="M136 112v106"/><path id="Line-Copy-10" fill="#C06334" stroke="#C06334" stroke-dasharray="1" stroke-linecap="square" stroke-width="2" d="M27 38l665 .5"/><path id="Line-Copy-11" fill="#C06334" stroke="#C06334" stroke-dasharray="1" stroke-linecap="square" stroke-width="2" d="M27 74.5h280"/><path id="Line-Copy-7" fill="#C06334" stroke="#C06334" stroke-dasharray="1" stroke-linecap="square" stroke-width="2" d="M254 132v86"/><path id="Line-Copy-8" fill="#C06334" stroke="#C06334" stroke-dasharray="1" stroke-linecap="square" stroke-width="2" d="M243 132v86"/><path id="Line-Copy-9" fill="#C06334" stroke="#C06334" stroke-dasharray="1" stroke-linecap="square" stroke-width="2" d="M308 38v180"/><path id="Line-Copy-13" fill="#C06334" stroke="#C06334" stroke-dasharray="1" stroke-linecap="square" stroke-width="2" d="M439 132v86"/><path id="Line-Copy-14" fill="#C06334" stroke="#C06334" stroke-dasharray="1" stroke-linecap="square" stroke-width="2" d="M622 132v86"/><path id="Line-Copy-15" fill="#C06334" stroke="#C06334" stroke-dasharray="1" stroke-linecap="square" stroke-width="2" d="M692 39v179"/><text id="href" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="337" y="33">href</tspan></text><text id="origin" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="135" y="68">origin</tspan></text><path id="Line-Copy-12" fill="#C06334" stroke="#C06334" stroke-dasharray="1" stroke-linecap="square" stroke-width="2" d="M136 112.5h170"/><text id="host" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="202" y="106">host</tspan></text><text id="protocol" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="30.6" y="152">protocol</tspan></text><path id="https://site.com:8080/path/page?p1=v1&p2=v2…#hash" fill="#181717" fill-rule="nonzero" d="M31.772 196.4h2.808v4.716h.09a4.03 4.03 0 011.296-.981c.492-.234 1.104-.351 1.836-.351.576 0 1.077.06 1.503.18.426.12.774.33 1.044.63s.471.711.603 1.233c.132.522.198 1.185.198 1.989V209h-1.404v-4.914c0-.516-.03-.966-.09-1.35-.06-.384-.177-.705-.351-.963a1.626 1.626 0 00-.711-.585c-.3-.132-.684-.198-1.152-.198a2.76 2.76 0 00-.954.171c-.312.114-.597.27-.855.468s-.48.438-.666.72a2.693 2.693 0 00-.387.927V209h-1.386v-11.394h-1.422V196.4zm13.686 3.6H47.6v-1.782l1.404-.396V200h4.806v1.206h-4.806v4.248c0 .876.213 1.524.639 1.944.426.42 1.035.63 1.827.63.54 0 1.011-.102 1.413-.306a7.614 7.614 0 001.089-.666l.468 1.062c-.42.336-.921.606-1.503.81a5.377 5.377 0 01-1.791.306c-.48 0-.933-.069-1.359-.207a3.12 3.12 0 01-1.125-.639 3.06 3.06 0 01-.774-1.107c-.192-.45-.288-.987-.288-1.611v-4.464h-2.142V200zm13.2 0H60.8v-1.782l1.404-.396V200h4.806v1.206h-4.806v4.248c0 .876.213 1.524.639 1.944.426.42 1.035.63 1.827.63.54 0 1.011-.102 1.413-.306a7.614 7.614 0 001.089-.666l.468 1.062c-.42.336-.921.606-1.503.81a5.377 5.377 0 01-1.791.306c-.48 0-.933-.069-1.359-.207a3.12 3.12 0 01-1.125-.639 3.06 3.06 0 01-.774-1.107c-.192-.45-.288-.987-.288-1.611v-4.464h-2.142V200zm14.37 3.15a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V200h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm15.216 5.472c0-.384-.15-.681-.45-.891-.3-.21-.672-.378-1.116-.504a20.643 20.643 0 00-1.449-.351 8.36 8.36 0 01-1.449-.432 3.247 3.247 0 01-1.116-.729c-.3-.306-.45-.735-.45-1.287 0-.456.099-.846.297-1.17.198-.324.459-.591.783-.801.324-.21.702-.366 1.134-.468a5.856 5.856 0 011.35-.153c.84 0 1.563.105 2.169.315.606.21 1.089.429 1.449.657l-.576 1.134a15.16 15.16 0 00-1.287-.621c-.462-.198-1.041-.297-1.737-.297-.264 0-.525.027-.783.081a2.767 2.767 0 00-.702.243c-.21.108-.378.249-.504.423a1.06 1.06 0 00-.189.639c0 .312.15.552.45.72.3.168.672.309 1.116.423.444.114.927.222 1.449.324a7.044 7.044 0 011.449.441c.444.192.816.45 1.116.774.3.324.45.762.45 1.314 0 .828-.327 1.512-.981 2.052-.654.54-1.635.81-2.943.81-.396 0-.786-.036-1.17-.108a7.096 7.096 0 01-1.08-.288 6.498 6.498 0 01-.918-.405 3.86 3.86 0 01-.684-.459l.72-1.17c.144.144.336.291.576.441a5.437 5.437 0 001.719.693 4.833 4.833 0 001.908.027c.282-.054.531-.138.747-.252.216-.114.387-.264.513-.45s.189-.411.189-.675zm9.51-5.634c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm0 7.074c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm17.124-11.826l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm13.2 0l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm12.966 10.386c0-.384-.15-.681-.45-.891-.3-.21-.672-.378-1.116-.504a20.643 20.643 0 00-1.449-.351 8.36 8.36 0 01-1.449-.432 3.247 3.247 0 01-1.116-.729c-.3-.306-.45-.735-.45-1.287 0-.456.099-.846.297-1.17.198-.324.459-.591.783-.801.324-.21.702-.366 1.134-.468a5.856 5.856 0 011.35-.153c.84 0 1.563.105 2.169.315.606.21 1.089.429 1.449.657l-.576 1.134a15.16 15.16 0 00-1.287-.621c-.462-.198-1.041-.297-1.737-.297-.264 0-.525.027-.783.081a2.767 2.767 0 00-.702.243c-.21.108-.378.249-.504.423a1.06 1.06 0 00-.189.639c0 .312.15.552.45.72.3.168.672.309 1.116.423.444.114.927.222 1.449.324a7.044 7.044 0 011.449.441c.444.192.816.45 1.116.774.3.324.45.762.45 1.314 0 .828-.327 1.512-.981 2.052-.654.54-1.635.81-2.943.81-.396 0-.786-.036-1.17-.108a7.096 7.096 0 01-1.08-.288 6.498 6.498 0 01-.918-.405 3.86 3.86 0 01-.684-.459l.72-1.17c.144.144.336.291.576.441a5.437 5.437 0 001.719.693 4.833 4.833 0 001.908.027c.282-.054.531-.138.747-.252.216-.114.387-.264.513-.45s.189-.411.189-.675zm7.206 2.43v-1.206h3.132v-6.588h-3.132V200h4.572v7.794h3.06V209h-7.632zm2.646-11.556c0-.324.108-.603.324-.837.216-.234.492-.351.828-.351.348 0 .639.117.873.351.234.234.351.513.351.837 0 .312-.117.576-.351.792a1.238 1.238 0 01-.873.324c-.336 0-.612-.108-.828-.324a1.077 1.077 0 01-.324-.792zm9.222 2.556h2.142v-1.782l1.404-.396V200h4.806v1.206h-4.806v4.248c0 .876.213 1.524.639 1.944.426.42 1.035.63 1.827.63.54 0 1.011-.102 1.413-.306a7.614 7.614 0 001.089-.666l.468 1.062c-.42.336-.921.606-1.503.81a5.377 5.377 0 01-1.791.306c-.48 0-.933-.069-1.359-.207a3.12 3.12 0 01-1.125-.639 3.06 3.06 0 01-.774-1.107c-.192-.45-.288-.987-.288-1.611v-4.464h-2.142V200zm22.164 7.866a4.53 4.53 0 01-.765.54 5.645 5.645 0 01-.963.432 7.06 7.06 0 01-1.089.279 6.633 6.633 0 01-1.143.099c-.72 0-1.356-.111-1.908-.333a3.674 3.674 0 01-1.386-.954 4.158 4.158 0 01-.846-1.485c-.192-.576-.288-1.224-.288-1.944 0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.504 0 1.002.066 1.494.198a2.95 2.95 0 011.305.747c.378.366.669.87.873 1.512.204.642.276 1.473.216 2.493h-6.966c0 1.08.291 1.887.873 2.421.582.534 1.359.801 2.331.801.324 0 .645-.039.963-.117.318-.078.621-.171.909-.279.288-.108.543-.228.765-.36.222-.132.393-.252.513-.36l.558 1.026zm-3.798-6.894a4.86 4.86 0 00-1.125.126 2.564 2.564 0 00-.936.423c-.27.198-.492.456-.666.774-.174.318-.285.711-.333 1.179h5.598c-.06-.792-.315-1.407-.765-1.845-.45-.438-1.041-.657-1.773-.657zm11.67 7.038c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm16.908-6.624a7.111 7.111 0 00-.891-.243 4.64 4.64 0 00-.945-.099c-1.176 0-2.052.276-2.628.828-.576.552-.864 1.428-.864 2.628 0 .528.084.999.252 1.413.168.414.408.765.72 1.053.312.288.687.51 1.125.666.438.156.921.234 1.449.234.564 0 1.113-.096 1.647-.288.534-.192.981-.444 1.341-.756l.63 1.044a5.095 5.095 0 01-.63.45 6.053 6.053 0 01-1.971.774 6.38 6.38 0 01-1.305.126c-.78 0-1.467-.111-2.061-.333a3.914 3.914 0 01-1.485-.954 4.083 4.083 0 01-.9-1.494 5.818 5.818 0 01-.306-1.935c0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.888 0 1.611.078 2.169.234.558.156 1.029.33 1.413.522l-.018.054v2.502h-1.296v-1.71zm6.324 3.114c0-1.452.378-2.601 1.134-3.447.756-.846 1.836-1.269 3.24-1.269.756 0 1.407.123 1.953.369s.999.579 1.359.999c.36.42.627.918.801 1.494.174.576.261 1.194.261 1.854 0 .72-.096 1.371-.288 1.953a4.057 4.057 0 01-.846 1.485 3.738 3.738 0 01-1.377.945c-.546.222-1.167.333-1.863.333-.744 0-1.392-.123-1.944-.369a3.815 3.815 0 01-1.368-.999 4.073 4.073 0 01-.801-1.494 6.381 6.381 0 01-.261-1.854zm1.494 0c0 .42.051.84.153 1.26.102.42.267.798.495 1.134.228.336.525.606.891.81.366.204.813.306 1.341.306.96 0 1.683-.297 2.169-.891.486-.594.729-1.467.729-2.619 0-.432-.051-.855-.153-1.269a3.353 3.353 0 00-.504-1.125 2.656 2.656 0 00-.9-.81c-.366-.204-.813-.306-1.341-.306-.96 0-1.68.294-2.16.882-.48.588-.72 1.464-.72 2.628zm15.396 4.5v-5.994a9.51 9.51 0 00-.027-.729 2.442 2.442 0 00-.126-.63 1.01 1.01 0 00-.288-.441.734.734 0 00-.495-.162c-.408 0-.753.168-1.035.504-.282.336-.495.756-.639 1.26V209h-1.368v-9h.936l.27 1.098h.072c.12-.18.237-.351.351-.513.114-.162.246-.303.396-.423.15-.12.327-.213.531-.279.204-.066.462-.099.774-.099.18 0 .366.027.558.081.192.054.369.138.531.252.162.114.303.267.423.459s.198.426.234.702c.276-.468.585-.834.927-1.098.342-.264.813-.396 1.413-.396.396 0 .717.066.963.198s.438.321.576.567c.138.246.234.54.288.882.054.342.081.723.081 1.143V209h-1.368v-6.12c0-.252-.012-.489-.036-.711a2.268 2.268 0 00-.135-.585.967.967 0 00-.27-.396c-.114-.096-.267-.144-.459-.144-.42 0-.774.168-1.062.504-.288.336-.504.816-.648 1.44V209h-1.368zm12.678-8.064c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm0 7.074c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm10.356-2.07c0-1.452.882-2.622 2.646-3.51-.336-.18-.648-.366-.936-.558a3.746 3.746 0 01-.747-.648 2.737 2.737 0 01-.495-.828 2.996 2.996 0 01-.18-1.08c0-.444.09-.858.27-1.242.18-.384.438-.714.774-.99a3.842 3.842 0 011.224-.657 4.988 4.988 0 011.602-.243c.552 0 1.053.072 1.503.216a3.5 3.5 0 011.152.603c.318.258.561.561.729.909.168.348.252.72.252 1.116 0 .684-.171 1.311-.513 1.881-.342.57-.891 1.089-1.647 1.557.348.18.672.372.972.576.3.204.561.432.783.684.222.252.396.537.522.855.126.318.189.681.189 1.089 0 .504-.09.975-.27 1.413a3 3 0 01-.801 1.125 3.88 3.88 0 01-1.305.738c-.516.18-1.116.27-1.8.27-.636 0-1.2-.09-1.692-.27a3.696 3.696 0 01-1.233-.72c-.33-.3-.579-.648-.747-1.044a3.148 3.148 0 01-.252-1.242zm6.678-.18c0-.348-.081-.657-.243-.927a2.74 2.74 0 00-.648-.729 5.59 5.59 0 00-.927-.594c-.348-.18-.708-.36-1.08-.54-.864.42-1.479.882-1.845 1.386-.366.504-.549.978-.549 1.422 0 .3.057.585.171.855.114.27.282.507.504.711.222.204.498.366.828.486.33.12.711.18 1.143.18.348 0 .681-.048.999-.144.318-.096.6-.237.846-.423s.441-.42.585-.702c.144-.282.216-.609.216-.981zm-4.986-6.498c0 .336.078.636.234.9.156.264.363.501.621.711.258.21.555.402.891.576.336.174.69.345 1.062.513.636-.42 1.113-.84 1.431-1.26.318-.42.477-.882.477-1.386 0-.3-.063-.567-.189-.801a1.948 1.948 0 00-.504-.603 2.292 2.292 0 00-.729-.387 2.77 2.77 0 00-.864-.135c-.384 0-.726.054-1.026.162-.3.108-.552.252-.756.432-.204.18-.363.381-.477.603a1.463 1.463 0 00-.171.675zm11.148 3.438c0-1.02.09-1.935.27-2.745.18-.81.45-1.494.81-2.052.36-.558.816-.984 1.368-1.278.552-.294 1.206-.441 1.962-.441.804 0 1.485.144 2.043.432a3.466 3.466 0 011.359 1.26c.348.552.6 1.233.756 2.043.156.81.234 1.737.234 2.781 0 1.02-.09 1.935-.27 2.745-.18.81-.45 1.494-.81 2.052-.36.558-.816.984-1.368 1.278-.552.294-1.206.441-1.962.441-.792 0-1.467-.159-2.025-.477a3.8 3.8 0 01-1.368-1.341c-.354-.576-.609-1.263-.765-2.061a13.774 13.774 0 01-.234-2.637zm7.326 0c0-.636-.036-1.242-.108-1.818l-5.346 4.878c.204.684.51 1.23.918 1.638.408.408.942.612 1.602.612 1.056 0 1.809-.438 2.259-1.314.45-.876.675-2.208.675-3.996zm-5.832 0c0 .3.009.588.027.864.018.276.039.546.063.81l5.364-4.86c-.204-.648-.507-1.164-.909-1.548-.402-.384-.945-.576-1.629-.576-1.068 0-1.821.441-2.259 1.323-.438.882-.657 2.211-.657 3.987zm12.066 3.24c0-1.452.882-2.622 2.646-3.51-.336-.18-.648-.366-.936-.558a3.746 3.746 0 01-.747-.648 2.737 2.737 0 01-.495-.828 2.996 2.996 0 01-.18-1.08c0-.444.09-.858.27-1.242.18-.384.438-.714.774-.99a3.842 3.842 0 011.224-.657 4.988 4.988 0 011.602-.243c.552 0 1.053.072 1.503.216a3.5 3.5 0 011.152.603c.318.258.561.561.729.909.168.348.252.72.252 1.116 0 .684-.171 1.311-.513 1.881-.342.57-.891 1.089-1.647 1.557.348.18.672.372.972.576.3.204.561.432.783.684.222.252.396.537.522.855.126.318.189.681.189 1.089 0 .504-.09.975-.27 1.413a3 3 0 01-.801 1.125 3.88 3.88 0 01-1.305.738c-.516.18-1.116.27-1.8.27-.636 0-1.2-.09-1.692-.27a3.696 3.696 0 01-1.233-.72c-.33-.3-.579-.648-.747-1.044a3.148 3.148 0 01-.252-1.242zm6.678-.18c0-.348-.081-.657-.243-.927a2.74 2.74 0 00-.648-.729 5.59 5.59 0 00-.927-.594c-.348-.18-.708-.36-1.08-.54-.864.42-1.479.882-1.845 1.386-.366.504-.549.978-.549 1.422 0 .3.057.585.171.855.114.27.282.507.504.711.222.204.498.366.828.486.33.12.711.18 1.143.18.348 0 .681-.048.999-.144.318-.096.6-.237.846-.423s.441-.42.585-.702c.144-.282.216-.609.216-.981zm-4.986-6.498c0 .336.078.636.234.9.156.264.363.501.621.711.258.21.555.402.891.576.336.174.69.345 1.062.513.636-.42 1.113-.84 1.431-1.26.318-.42.477-.882.477-1.386 0-.3-.063-.567-.189-.801a1.948 1.948 0 00-.504-.603 2.292 2.292 0 00-.729-.387 2.77 2.77 0 00-.864-.135c-.384 0-.726.054-1.026.162-.3.108-.552.252-.756.432-.204.18-.363.381-.477.603a1.463 1.463 0 00-.171.675zm11.148 3.438c0-1.02.09-1.935.27-2.745.18-.81.45-1.494.81-2.052.36-.558.816-.984 1.368-1.278.552-.294 1.206-.441 1.962-.441.804 0 1.485.144 2.043.432a3.466 3.466 0 011.359 1.26c.348.552.6 1.233.756 2.043.156.81.234 1.737.234 2.781 0 1.02-.09 1.935-.27 2.745-.18.81-.45 1.494-.81 2.052-.36.558-.816.984-1.368 1.278-.552.294-1.206.441-1.962.441-.792 0-1.467-.159-2.025-.477a3.8 3.8 0 01-1.368-1.341c-.354-.576-.609-1.263-.765-2.061a13.774 13.774 0 01-.234-2.637zm7.326 0c0-.636-.036-1.242-.108-1.818l-5.346 4.878c.204.684.51 1.23.918 1.638.408.408.942.612 1.602.612 1.056 0 1.809-.438 2.259-1.314.45-.876.675-2.208.675-3.996zm-5.832 0c0 .3.009.588.027.864.018.276.039.546.063.81l5.364-4.86c-.204-.648-.507-1.164-.909-1.548-.402-.384-.945-.576-1.629-.576-1.068 0-1.821.441-2.259 1.323-.438.882-.657 2.211-.657 3.987zm18.834-6.516l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm6.81 6.966a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V200h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm9.276-.396a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V209h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zm9.168-7.956h2.142v-1.782l1.404-.396V200h4.806v1.206h-4.806v4.248c0 .876.213 1.524.639 1.944.426.42 1.035.63 1.827.63.54 0 1.011-.102 1.413-.306a7.614 7.614 0 001.089-.666l.468 1.062c-.42.336-.921.606-1.503.81a5.377 5.377 0 01-1.791.306c-.48 0-.933-.069-1.359-.207a3.12 3.12 0 01-1.125-.639 3.06 3.06 0 01-.774-1.107c-.192-.45-.288-.987-.288-1.611v-4.464h-2.142V200zm12.714-3.6h2.808v4.716h.09a4.03 4.03 0 011.296-.981c.492-.234 1.104-.351 1.836-.351.576 0 1.077.06 1.503.18.426.12.774.33 1.044.63s.471.711.603 1.233c.132.522.198 1.185.198 1.989V209h-1.404v-4.914c0-.516-.03-.966-.09-1.35-.06-.384-.177-.705-.351-.963a1.626 1.626 0 00-.711-.585c-.3-.132-.684-.198-1.152-.198a2.76 2.76 0 00-.954.171c-.312.114-.597.27-.855.468s-.48.438-.666.72a2.693 2.693 0 00-.387.927V209h-1.386v-11.394h-1.422V196.4zm21.246-.216l1.134.504-6.57 14.832-1.134-.504 6.57-14.832zm6.81 6.966a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V200h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm9.276-.396a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V209h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zm18.042 1.494c0 .564-.108 1.059-.324 1.485a2.917 2.917 0 01-.891 1.053 4.02 4.02 0 01-1.323.621 6.185 6.185 0 01-1.638.207c-.708 0-1.326-.072-1.854-.216a5.515 5.515 0 01-1.368-.558l.684-1.278c.108.096.249.189.423.279.174.09.375.174.603.252.228.078.471.141.729.189.258.048.513.072.765.072.528 0 .969-.048 1.323-.144.354-.096.639-.246.855-.45.216-.204.372-.468.468-.792.096-.324.144-.72.144-1.188v-1.008h-.072c-.276.396-.63.702-1.062.918-.432.216-.99.324-1.674.324-1.368 0-2.373-.381-3.015-1.143-.642-.762-.963-1.947-.963-3.555 0-1.536.408-2.697 1.224-3.483.816-.786 2.028-1.179 3.636-1.179.744 0 1.377.051 1.899.153.522.102.999.231 1.431.387v9.054zm-4.014-1.44c.732 0 1.308-.189 1.728-.567.42-.378.714-.957.882-1.737v-4.266c-.528-.252-1.266-.378-2.214-.378-.96 0-1.716.282-2.268.846-.552.564-.828 1.428-.828 2.592 0 .516.048.987.144 1.413.096.426.249.795.459 1.107.21.312.486.555.828.729.342.174.765.261 1.269.261zm17.304-.144a4.53 4.53 0 01-.765.54 5.645 5.645 0 01-.963.432 7.06 7.06 0 01-1.089.279 6.633 6.633 0 01-1.143.099c-.72 0-1.356-.111-1.908-.333a3.674 3.674 0 01-1.386-.954 4.158 4.158 0 01-.846-1.485c-.192-.576-.288-1.224-.288-1.944 0-.756.105-1.428.315-2.016.21-.588.513-1.08.909-1.476a3.959 3.959 0 011.44-.909c.564-.21 1.194-.315 1.89-.315.504 0 1.002.066 1.494.198a2.95 2.95 0 011.305.747c.378.366.669.87.873 1.512.204.642.276 1.473.216 2.493h-6.966c0 1.08.291 1.887.873 2.421.582.534 1.359.801 2.331.801.324 0 .645-.039.963-.117.318-.078.621-.171.909-.279.288-.108.543-.228.765-.36.222-.132.393-.252.513-.36l.558 1.026zm-3.798-6.894a4.86 4.86 0 00-1.125.126 2.564 2.564 0 00-.936.423c-.27.198-.492.456-.666.774-.174.318-.285.711-.333 1.179h5.598c-.06-.792-.315-1.407-.765-1.845-.45-.438-1.041-.657-1.773-.657zm11.652 4.842a1.011 1.011 0 01-.018-.198v-.198c0-.504.093-.936.279-1.296.186-.36.417-.681.693-.963a6.74 6.74 0 01.891-.765c.318-.228.615-.471.891-.729s.507-.543.693-.855c.186-.312.279-.684.279-1.116 0-.228-.054-.474-.162-.738a2.314 2.314 0 00-.495-.738 2.501 2.501 0 00-.864-.558c-.354-.144-.777-.216-1.269-.216a3.7 3.7 0 00-1.071.144 4.343 4.343 0 00-.864.36 4.644 4.644 0 00-.702.477c-.21.174-.399.339-.567.495l-.918-.828a4.62 4.62 0 011.953-1.431c.798-.318 1.713-.477 2.745-.477.624 0 1.17.102 1.638.306.468.204.855.465 1.161.783.306.318.534.672.684 1.062.15.39.225.771.225 1.143 0 .576-.099 1.059-.297 1.449-.198.39-.447.738-.747 1.044-.3.306-.621.582-.963.828-.342.246-.663.51-.963.792-.3.282-.549.597-.747.945-.198.348-.297.774-.297 1.278h-1.188zm-.36 2.358c0-.324.093-.579.279-.765.186-.186.435-.279.747-.279.324 0 .582.093.774.279.192.186.288.441.288.765 0 .312-.096.564-.288.756-.192.192-.45.288-.774.288-.312 0-.561-.096-.747-.288-.186-.192-.279-.444-.279-.756zm11.112-5.022a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V200h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.561 6.561 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm9.618 6.606h2.826v-9.468l-2.88 2.016-.702-1.026 3.96-2.826h1.008v11.304h2.772V209h-6.984v-1.296zm12.138-6.93h8.352v1.296h-8.352v-1.296zm0 2.952h8.352v1.296h-8.352v-1.296zm17.412 3.528h.198L502.16 200h1.566l-3.798 9h-1.584l-3.87-9h1.656l3.006 7.254zm10.05.45h2.826v-9.468l-2.88 2.016-.702-1.026 3.96-2.826h1.008v11.304h2.772V209h-6.984v-1.296zm11.454-2.43c0-.792.222-1.548.666-2.268.444-.72 1.062-1.326 1.854-1.818a11.046 11.046 0 01-.747-1.296c-.21-.432-.315-.9-.315-1.404 0-.288.048-.57.144-.846.096-.276.249-.522.459-.738.21-.216.48-.39.81-.522.33-.132.729-.198 1.197-.198.492 0 .906.063 1.242.189.336.126.606.291.81.495.204.204.351.432.441.684.09.252.135.51.135.774 0 .504-.195 1.023-.585 1.557-.39.534-1.005 1.047-1.845 1.539.192.348.417.705.675 1.071.258.366.531.729.819 1.089.288.36.582.717.882 1.071.3.354.6.681.9.981a3.19 3.19 0 00.351-.702c.102-.276.186-.564.252-.864a8.368 8.368 0 00.189-1.728h1.278a10.485 10.485 0 01-.36 2.259c-.204.75-.504 1.383-.9 1.899a5.657 5.657 0 001.35 1.188v1.53c-.636-.312-1.362-.87-2.178-1.674-.216.216-.435.423-.657.621a4.088 4.088 0 01-1.692.909 4.734 4.734 0 01-1.233.144c-.492 0-.972-.081-1.44-.243a3.787 3.787 0 01-1.26-.729 3.608 3.608 0 01-.9-1.224c-.228-.492-.342-1.074-.342-1.746zm6.678 1.368c-.348-.348-.69-.726-1.026-1.134a34.295 34.295 0 01-1.809-2.403 30.16 30.16 0 01-.657-.999c-.648.492-1.101.99-1.359 1.494a3.505 3.505 0 00-.387 1.62c0 .468.087.873.261 1.215.174.342.393.627.657.855.264.228.555.396.873.504.318.108.627.162.927.162.6 0 1.101-.135 1.503-.405.402-.27.741-.573 1.017-.909zm-3.816-8.154c0 .36.075.717.225 1.071.15.354.345.705.585 1.053.636-.408 1.077-.78 1.323-1.116.246-.336.369-.63.369-.882 0-.384-.093-.696-.279-.936-.186-.24-.507-.36-.963-.36-.432 0-.75.108-.954.324-.204.216-.306.498-.306.846zm11.526 4.662a18.063 18.063 0 00-.081-1.512 5.988 5.988 0 00-.063-.468h-1.206V200h2.394l.18 1.26h.09a4.06 4.06 0 011.233-1.053c.498-.282 1.107-.423 1.827-.423 1.284 0 2.25.354 2.898 1.062.648.708.972 1.866.972 3.474 0 .756-.111 1.437-.333 2.043a4.295 4.295 0 01-.945 1.539c-.408.42-.9.744-1.476.972a5.141 5.141 0 01-1.908.342c-.252 0-.477-.009-.675-.027a5.93 5.93 0 01-.54-.072 4.255 4.255 0 01-.477-.117 6.562 6.562 0 01-.504-.18v3.78h-1.386v-9.45zm4.14-2.052c-.36 0-.699.066-1.017.198a3.034 3.034 0 00-1.449 1.242 2.118 2.118 0 00-.288.792v4.104a3.3 3.3 0 00.891.423c.33.102.765.153 1.305.153.96 0 1.728-.315 2.304-.945.576-.63.864-1.551.864-2.763 0-1.02-.201-1.809-.603-2.367-.402-.558-1.071-.837-2.007-.837zm16.332-1.512c0 1.104-.522 2.334-1.566 3.69-1.044 1.356-2.502 2.832-4.374 4.428h6.336V209h-7.992v-1.296c.228-.216.54-.498.936-.846a57.05 57.05 0 001.278-1.161c.456-.426.921-.885 1.395-1.377a15.64 15.64 0 001.287-1.512 8.867 8.867 0 00.936-1.548c.24-.516.36-1.014.36-1.494 0-.72-.189-1.287-.567-1.701-.378-.414-.945-.621-1.701-.621-.648 0-1.194.072-1.638.216a3.988 3.988 0 00-1.206.63l-.612-.99a5.806 5.806 0 011.719-.846 6.962 6.962 0 011.989-.27c1.128 0 1.98.306 2.556.918.576.612.864 1.44.864 2.484zm5.424 1.188h8.352v1.296h-8.352v-1.296zm0 2.952h8.352v1.296h-8.352v-1.296zm17.412 3.528h.198L581.36 200h1.566l-3.798 9h-1.584l-3.87-9h1.656l3.006 7.254zm16.764-7.668c0 1.104-.522 2.334-1.566 3.69-1.044 1.356-2.502 2.832-4.374 4.428h6.336V209h-7.992v-1.296c.228-.216.54-.498.936-.846s.822-.735 1.278-1.161c.456-.426.921-.885 1.395-1.377a15.64 15.64 0 001.287-1.512 8.867 8.867 0 00.936-1.548c.24-.516.36-1.014.36-1.494 0-.72-.189-1.287-.567-1.701-.378-.414-.945-.621-1.701-.621-.648 0-1.194.072-1.638.216a3.988 3.988 0 00-1.206.63l-.612-.99a5.806 5.806 0 011.719-.846 6.962 6.962 0 011.989-.27c1.128 0 1.98.306 2.556.918.576.612.864 1.44.864 2.484zm12.966 8.424c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm-4.572 0c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm-4.536 0c0-.384.108-.681.324-.891.216-.21.504-.315.864-.315.384 0 .684.105.9.315.216.21.324.507.324.891 0 .348-.108.636-.324.864-.216.228-.516.342-.9.342-.36 0-.648-.114-.864-.342-.216-.228-.324-.516-.324-.864zm32.898-3.06h-2.61l-.702 3.006h-1.242l.702-3.006h-1.764l.252-1.152h1.782l.558-2.358h-1.692l.252-1.152h1.71l.684-2.844h1.242l-.684 2.844h2.61l.684-2.844h1.242l-.684 2.844h1.746l-.288 1.152h-1.728l-.558 2.358h1.674l-.288 1.152h-1.656l-.702 3.006h-1.242l.702-3.006zm-2.34-1.152h2.61l.558-2.358h-2.61l-.558 2.358zm9.456-7.398h2.808v4.716h.09a4.03 4.03 0 011.296-.981c.492-.234 1.104-.351 1.836-.351.576 0 1.077.06 1.503.18.426.12.774.33 1.044.63s.471.711.603 1.233c.132.522.198 1.185.198 1.989V209h-1.404v-4.914c0-.516-.03-.966-.09-1.35-.06-.384-.177-.705-.351-.963a1.626 1.626 0 00-.711-.585c-.3-.132-.684-.198-1.152-.198a2.76 2.76 0 00-.954.171c-.312.114-.597.27-.855.468s-.48.438-.666.72a2.693 2.693 0 00-.387.927V209h-1.386v-11.394h-1.422V196.4zm15.072 4.302a5.717 5.717 0 011.746-.648 9.544 9.544 0 011.908-.198c.612 0 1.113.096 1.503.288.39.192.696.441.918.747.222.306.372.654.45 1.044.078.39.117.783.117 1.179 0 .456-.012.942-.036 1.458a66.688 66.688 0 00-.054 1.548c0 .6.036 1.17.108 1.71h1.206V209h-2.394l-.162-1.35h-.09c-.072.108-.18.246-.324.414a2.964 2.964 0 01-.567.495 3.664 3.664 0 01-.855.423c-.336.12-.732.18-1.188.18-.888 0-1.59-.228-2.106-.684-.516-.456-.774-1.08-.774-1.872 0-.612.135-1.122.405-1.53.27-.408.657-.72 1.161-.936.504-.216 1.113-.342 1.827-.378.714-.036 1.515.012 2.403.144.06-.552.069-1.011.027-1.377-.042-.366-.138-.657-.288-.873a1.206 1.206 0 00-.63-.459c-.27-.09-.603-.135-.999-.135-.54 0-1.056.075-1.548.225-.492.15-.93.303-1.314.459l-.45-1.044zm2.646 7.254c.336 0 .648-.054.936-.162.288-.108.54-.246.756-.414a2.743 2.743 0 00.864-1.116v-1.26c-.624-.108-1.2-.162-1.728-.162s-.984.057-1.368.171c-.384.114-.684.291-.9.531-.216.24-.324.552-.324.936 0 .396.135.741.405 1.035.27.294.723.441 1.359.441zm16.494-1.386c0-.384-.15-.681-.45-.891-.3-.21-.672-.378-1.116-.504a20.643 20.643 0 00-1.449-.351 8.36 8.36 0 01-1.449-.432 3.247 3.247 0 01-1.116-.729c-.3-.306-.45-.735-.45-1.287 0-.456.099-.846.297-1.17.198-.324.459-.591.783-.801.324-.21.702-.366 1.134-.468a5.856 5.856 0 011.35-.153c.84 0 1.563.105 2.169.315.606.21 1.089.429 1.449.657l-.576 1.134a15.16 15.16 0 00-1.287-.621c-.462-.198-1.041-.297-1.737-.297-.264 0-.525.027-.783.081a2.767 2.767 0 00-.702.243c-.21.108-.378.249-.504.423a1.06 1.06 0 00-.189.639c0 .312.15.552.45.72.3.168.672.309 1.116.423.444.114.927.222 1.449.324a7.044 7.044 0 011.449.441c.444.192.816.45 1.116.774.3.324.45.762.45 1.314 0 .828-.327 1.512-.981 2.052-.654.54-1.635.81-2.943.81-.396 0-.786-.036-1.17-.108a7.096 7.096 0 01-1.08-.288 6.498 6.498 0 01-.918-.405 3.86 3.86 0 01-.684-.459l.72-1.17c.144.144.336.291.576.441a5.437 5.437 0 001.719.693 4.833 4.833 0 001.908.027c.282-.054.531-.138.747-.252.216-.114.387-.264.513-.45s.189-.411.189-.675zm5.388-10.17h2.808v4.716h.09a4.03 4.03 0 011.296-.981c.492-.234 1.104-.351 1.836-.351.576 0 1.077.06 1.503.18.426.12.774.33 1.044.63s.471.711.603 1.233c.132.522.198 1.185.198 1.989V209h-1.404v-4.914c0-.516-.03-.966-.09-1.35-.06-.384-.177-.705-.351-.963a1.626 1.626 0 00-.711-.585c-.3-.132-.684-.198-1.152-.198a2.76 2.76 0 00-.954.171c-.312.114-.597.27-.855.468s-.48.438-.666.72a2.693 2.693 0 00-.387.927V209h-1.386v-11.394h-1.422V196.4z"/><text id="hostname" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="147" y="152">hostname</tspan></text><text id="port" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="261" y="152">port</tspan></text><text id="pathname" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="334" y="152">pathname</tspan></text><text id="search" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="496" y="152">search</tspan></text><text id="hash" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="637" y="152">hash</tspan></text></g></g></svg> \ No newline at end of file diff --git a/5-network/08-xmlhttprequest/article.md b/5-network/08-xmlhttprequest/article.md new file mode 100644 index 0000000000..1397347af7 --- /dev/null +++ b/5-network/08-xmlhttprequest/article.md @@ -0,0 +1,526 @@ +# XMLHttpRequest + +`XMLHttpRequest` は JavaScript で HTTP リクエストを行うための組み込みのブラウザオブジェクトです。 + +名前に "XML" という用語を含んでいますが、XML 形式だけでなくあらゆるデータ扱うことができます。ファイルをアップロード/ダウンロードしたり、進捗の追跡など様々なことができます。 + +現在は `XMLHttpRequest` を若干非推奨とする、よりモダンなメソッド `fetch` があります。 + +モダンweb開発では、`XMLHttpRequest` は次の3つの理由で使われることがあります。: + +1. 歴史的な理由: `XMLHttpRequest` をもつ既存のスクリプトをサポートする必要がある場合 +2. 古いブラウザをサポートする必要があるが、 polyfill は使いたくない(e.g. スクリプトのサイズを小さくしたい)場合 +3. `fetch` がまだできないことをしたい場合. e.g アップロードの進捗を追跡するなど + +このような要件を聞いたことがありますか?もしそうなら `XMLHttpRequest` に進んでください。そうでなければ、<info:fetch> に進むのがよいでしょう。 + +## 基本 + +XMLHttpRequest には2つの操作モードがあります: 同期と非同期です。 + +先に、ほとんどのケースで使われる非同期を見ていきましょう。 + +リクエストをするためには、次の3ステップが必要です: + +1. `XMLHttpRequest` を作成します: + ```js + let xhr = new XMLHttpRequest(); // コンストラクタは引数なし + ``` + +2. 初期化をします: + ```js + xhr.open(method, URL, [async, user, password]) + ``` + + このメソッドは通常 `new XMLHttpRequest` のすぐ後で呼ばれ、リクエストのメインのパラメータを指定します。: + + - `method` -- HTTPメソッド. たいてい `"GET"` か `"POST"` です. + - `URL` -- リクエストURL。文字列で、[URL](info:url) オブジェクトもOKです。 + - `async` -- 明示的に `false` が指定されている場合、リクエストは同期になります。これについては後ほど説明します。 + - `user`, `password` -- ベーシック HTTP 認証のユーザとパスワードです(必要に応じて). + + `open` 呼び出しに注意してください。その名前とは対照的に、接続をオープンするわけではありません。リクエストを設定するだけで、ネットワーク処理は `send` 呼び出しでのみ始まります。 + +3. それを送ります + + ```js + xhr.send([body]) + ``` + + このメソッドは接続をオープンし、リクエストをサーバに送信します。オプションの `body` パラメータにはリクエストボディが含まれます。 + + `GET` のようないくつかのリクエストメソッドは body を持ちません。また `POST` などはデータをサーバに送信するのに `body` を使います。後ほど例を見ていきます。 + +4. 応答に対するイベントをリッスンします + + これら3つがもっとも広く使われています: + - `load` -- 結果が準備できたとき。404 のような HTTP エラーを含みます。 + - `error` -- リクエストが送信できなかったとき e.g. ネットワークダウン or URL不正 + - `progress` -- ダウンロード中に定期的にトリガーされ、ダウンロードされた量が確認できます。 + + ```js + xhr.onload = function() { + alert(`Loaded: ${xhr.status} ${xhr.response}`); + }; + + xhr.onerror = function() { // リクエストがまったく送信できなかったときにだけトリガーされます。 + alert(`Network Error`); + }; + + xhr.onprogress = function(event) { // 定期的にトリガーされます + // event.loaded - ダウンロードされたバイト + // event.lengthComputable = サーバが Content-Length ヘッダを送信した場合は true + // event.total - トータルのバイト数(lengthComputable が true の場合) + alert(`Received ${event.loaded} of ${event.total}`); + }; + ``` + +これは完全な例です。下のコードはサーバから `/article/xmlhttprequest/example/load` のURLをロードし、進行状況を表示します。: + +```js run +// 1. new XMLHttpRequest オブジェクトを作成 +let xhr = new XMLHttpRequest(); + +// 2. 設定: URL /article/.../load に対する GET-リクエスト +xhr.open('GET', '/article/xmlhttprequest/example/load'); + +// 3. ネットワーク経由でリクエスト送信 +xhr.send(); + +// 4. レスポンスを受け取った後に呼び出されます +xhr.onload = function() { + if (xhr.status != 200) { // レスポンスの HTTP ステータスを解析 + alert(`Error ${xhr.status}: ${xhr.statusText}`); // e.g. 404: Not Found + } else { // show the result + alert(`Done, got ${xhr.response.length} bytes`); // responseText is the server + } +}; + +xhr.onprogress = function(event) { + if (event.lengthComputable) { + alert(`Received ${event.loaded} of ${event.total} bytes`); + } else { + alert(`Received ${event.loaded} bytes`); // no Content-Length + } + +}; + +xhr.onerror = function() { + alert("Request failed"); +}; +``` + +サーバーが応答すると、リクエストオブジェクトの次のプロパティで結果を受け取ることができます。: + +`status` +: HTTPステータスコード(数値): `200`, `404`, `403` など。HTTP 以外の失敗の場合は `0` になります。 + +`statusText` +:HTTPステータスメッセージ(文字列): 通常, `200` の場合は `OK`、`404` の場合は `Not Fount`、`403` の場合は `Forbidden` など。 + +`response`(古いスクリプトは` responseText`を使用する場合があります) +:サーバーのレスポンス。 + +対応するプロパティを使用してタイムアウトを指定することもできます。: + +```js +xhr.timeout = 10000; // ms でのタイムアウト, これは 10 秒 +``` + +リクエストが指定時間内で成功しない場合はキャンセルされ、`timeout` イベントが発生します。 + +````smart header="URL 検索パラメータ" +`?name=value` のような URL パラメータを渡しつつ、適切なエンコーディングを保証するには、[URL](info:url) オブジェクトが使えます。: + +```js +let url = new URL('https://google.com/search'); +url.searchParams.set('q', 'test me!'); + +// パラメータ `q` はエンコードされます +xhr.open('GET', url); // https://google.com/search?q=test+me%21 +``` + +```` + +## レスポンスタイプ + +レスポンスの形式を設定するには `xhr.responseType` を使います。: + +- `""` (デフォルト) -- 文字列として取得, +- `"text"` -- 文字列として取得, +- `"arraybuffer"` -- `ArrayBuffer` として取得(バリナリデータに対して, チャプター <info:arraybuffer-binary-arrays> を参照), +- `"blob"` -- `Blob` として取得 (バイナリデータに対して, チャプター <info:blob> を参照), +- `"document"` -- XML ドキュメントとして取得 (XPath と他の XML メソッドを使うことができます), +- `"json"` -- JSON として取得 (自動的にパースされます). + +例えば、JSON としてレスポンスを取得してみましょう: + +```js run +let xhr = new XMLHttpRequest(); + +xhr.open('GET', '/article/xmlhttprequest/example/json'); + +*!* +xhr.responseType = 'json'; +*/!* + +xhr.send(); + +// レスポンスは {"message": "Hello, world!"} +xhr.onload = function() { + let responseObj = xhr.response; + alert(responseObj.message); // Hello, world! +}; +``` + +```smart +昔のスクリプトには、`xhr.responseText` や `xhr.responseXML` プロパティがあるかもしれません。 + +これらは、文字列や XML ドキュメントを取得するために歴史的な理由から存在しています。最近では、`xhr.responseType` で形式を設定して、上のように `xhr.response` を取得するべきです。 +``` + +## Ready states + +`XMLHttpRequest` は状況が進むにつれ、状態が変化します。現在の状態は `xhr.readyState` でアクセスできます。 + + +すべての状態は [仕様](https://xhr.spec.whatwg.org/#states) にあります: + +```js +UNSENT = 0; // 初期状態 +OPENED = 1; // open が呼ばれた +HEADERS_RECEIVED = 2; // レスポンスヘッダを受け取った +LOADING = 3; // レスポンスはロード中 +DONE = 4; // リクエスト完了 +``` + +`XMLHttpRequest` オブジェクトは `0` -> `1` -> `2` -> `3` -> ... -> `3` -> `4` の順番で遷移します。状態 `3` はネットワーク越しにデータパケットを受け取るたびに繰り返されます。 + +`readystatechange` イベントを使って追跡することができます: + +```js +xhr.onreadystatechange = function() { + if (xhr.readyState == 3) { + // loading + } + if (xhr.readyState == 4) { + // request finished + } +}; +``` + +`readystatechange` リスナーは本当に古いコードで見つけることができます。当時は `load` やその他のイベントがなかったという歴史的な理由です。 + +最近では `load/error/progress` ハンドラを使います。 + +## リクエストを中止する + +リクエストはいつでも終了できます。`xhr.abort()` 呼び出しはそれを行います: + +```js +xhr.abort(); // リクエストを終了する +``` + +これは `abort` イベントを発生させます。そして `xhr.status` は `0` になります。 + +## 同期リクエスト + +`open` メソッドの3番目のパラメータ `async` が `false` が設定されていた場合、リクエストは同期になります。 + +つまり、JavaScript の実行は `send()` で止まり、レスポンスが返ってきたときに再開されます。`alert` や `prompt` コマンドにやや似ています。 + +これは `open` の3番目のパラメータを `false` に書き換えた例です: + +```js +let xhr = new XMLHttpRequest(); + +xhr.open('GET', '/article/xmlhttprequest/hello.txt', *!*false*/!*); + +try { + xhr.send(); + if (xhr.status != 200) { + alert(`Error ${xhr.status}: ${xhr.statusText}`); + } else { + alert(xhr.response); + } +} catch(err) { // onerror の代わり + alert("Request failed"); +} +``` + +問題なく見えるかもしれませんが、同期呼び出しはめったに使われません。なぜなら読み込みが完了するまでページ内の JavaScript をブロックするからです。ブラウザによっては、スクロールができなくなります。また、同期呼び出しに時間がかかりすぎると、ブラウザは "ハングしている" web ページを閉じるよう提案することがあります。 + +別ドメインからのリクエストやタイムアウトの指定など、`XMLHttpRequest` の多くの高度な機能は同期リクエストでは使えません。また、ご覧の通り進行状況もありません。 + +したがって、同期リクエストはあまり使われないので、これ以上取り上げないでおきます。 + +## HTTP ヘッダ + +`XMLHttpRequest` はカスタムヘッダの送信とレスポンスからのヘッダ読み取り、両方が可能です。 + +HTTP ヘッダに関しては3つのメソッドがあります。: + +`setRequestHeader(name, value)` +: 指定された `name` と `value` のリクエストヘッダを設定します。 + + 例: + + ```js + xhr.setRequestHeader('Content-Type', 'application/json'); + ``` + + ```warn header="ヘッダの制限" + いくつかのヘッダはブラウザだけが管理しています。例えば、`Referer` や `Host` です。 + 完全なリストは [仕様](http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader-method) にあります。 + + ユーザの安全性やリクエストの正当性の観点から、`XMLHttpRequest` ではそれらを変更することは許可されていません。 ``` + + ````warn header="ヘッダを削除することはできません" + `XMLHttpRequest` のもう一つの特徴は `setRequestHeader` を取り消すことはできないということです。 + + 一度ヘッダを設定すると、それが設定されます。さらなる呼び出しはヘッダへの情報の追加であり、上書きでは有りません。 + + 例: + + ```js + xhr.setRequestHeader('X-Auth', '123'); + xhr.setRequestHeader('X-Auth', '456'); + + // ヘッダはこうなります: + // X-Auth: 123, 456 + ``` + ```` + +`getResponseHeader(name)` +: 指定された `name` (`Set-Cookie` と `Set-Cookie2` は除く) のレスポンスヘッダを取得します。 + + 例: + + ```js + xhr.getResponseHeader('Content-Type') + ``` + +`getAllResponseHeaders()` +: `Set-Cookie` と `Set-Cookie2` を除く、すべてのレスポンスヘッダを返します。 + + ヘッダは次のように1行で返却されます。: + + ``` + Cache-Control: max-age=31536000 + Content-Length: 4260 + Content-Type: image/png + Date: Sat, 08 Sep 2012 16:53:16 GMT + ``` + + ヘッダ間の改行は常に `"\r\n"` です(OSに依存しません)。なので、簡単に個々のヘッダに分割することができます。名前と値のセパレータは常にコロンとそれに続くスペースです `": "`。これは仕様で決められています。 + + なので、name/value のペアをもつオブジェクトを取得したい場合は少し JS が必要になります。 + + 例えばこのようになります(2つのヘッダの名前が同じ場合、前者のヘッダが後者のヘッダで上書きされる想定です): + + ```js + let headers = xhr + .getAllResponseHeaders() + .split('\r\n') + .reduce((result, current) => { + let [name, value] = current.split(': '); + result[name] = value; + return result; + }, {}); + ``` + +## POST, FormData + +POST リクエストをするには、組み込みの [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) オブジェクトを使います。 + +構文: + +```js +let formData = new FormData([form]); // オブジェクトを作成します。オプションで <form> を指定します +formData.append(name, value); // フィールドを追加します +``` + +オプションでフォームから作成し、必要に応じて "追加" フィールドを追加します。その後: + +1. `xhr.open('POST', ...)` – `POST` メソッドを使います +2. `xhr.send(formData)` で、フォームをサーバに送信します + +例: + +```html run +<form name="person"> + <input name="name" value="John"> + <input name="surname" value="Smith"> +</form> + +<script> + // pre-fill FormData from the form + let formData = new FormData(document.forms.person); + + // add one more field + formData.append("middle", "Lee"); + + // send it out + let xhr = new XMLHttpRequest(); + xhr.open("POST", "/article/xmlhttprequest/post/user"); + xhr.send(formData); + +</script> +``` + +フォームは `multipart/form-data` エンコーディングで送信されます。 + +あるいは、JSON を好むなら `JSON.stringify` をして、文字列として送信します。 + +ヘッダ `Content-Type: application/json` を設定するのを忘れないでください。多くのサーバサイド側のフレームワークはそれで自動的に JSON をデコードしいます。: + +```js +let xhr = new XMLHttpRequest(); + +let json = JSON.stringify({ + name: "John", + surname: "Smith" +}); + +xhr.open("POST", '/submit') +xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8'); + +xhr.send(json); +``` + +`.send(body)` メソッドは非常に雑食です。`Blob` や `BufferSource` オブジェクトを含め、ほぼなんでも送信できます。 + + +## アップロードの進行状況 + +`progress` イベントはダウンロードの段階でのみ機能します。 + +つまり: なにかを `POST` したとき、`XMLHttpRequest` は最初にデータ(リクエストボディ)をアップロードし、次にレスポンスをダウンロードします。 + +なにか大きなものをアップロードする場合、アップロードの進行状況を追跡することはやりたいことの一つです。ですが、`xhr.onprogress` はここでは役に立ちません。 + +別のオブジェクト `xhr.upload` があります。これはアップロードイベント専用でメソッドを持ちません。 + +イベントの一覧は `xhr` イベントに似ていますが、`xhr.upload` アップロード時にそれらを発生させます。: + +- `loadstart` -- アップロード開始 +- `progress` -- アップロード中、定期的に発生します +- `abort` -- アップロード中止 +- `error` -- 非 HTTP エラー +- `load` -- アップロードが正常に終了 +- `timeout` -- アップロードのタイムアウト(`timeout` プロパティが設定されている場合 +- `loadend` -- アップロードが成功/失敗関係なく終了 + +ハンドラの例です: + +```js +xhr.upload.onprogress = function(event) { + alert(`Uploaded ${event.loaded} of ${event.total} bytes`); +}; + +xhr.upload.onload = function() { + alert(`Upload finished successfully.`); +}; + +xhr.upload.onerror = function() { + alert(`Error during the upload: ${xhr.status}`); +}; +``` + +これは実際の例です: 進行状況を示すファイルのアップロードです: + +```html run +<input type="file" onchange="upload(this.files[0])"> + +<script> +function upload(file) { + let xhr = new XMLHttpRequest(); + + // アップロードの進行状況を追跡します +*!* + xhr.upload.onprogress = function(event) { + console.log(`Uploaded ${event.loaded} of ${event.total}`); + }; +*/!* + + // 追跡完了: 成功したか失敗した + xhr.onloadend = function() { + if (xhr.status == 200) { + console.log("success"); + } else { + console.log("error " + this.status); + } + }; + + xhr.open("POST", "/article/xmlhttprequest/post/upload"); + xhr.send(file); +} +</script> +``` + +## クロスオリジンリクエスト + +`XMLHttpRequest` は、[fetch](info:fetch-crossorigin) と同じ CORS ポシしーを使用して、クロスドメインリクエストを作ることができます。 + +`fetch` のように、デフォルトでは Cookie と HTTP 認証を別のオリジンへは送信しません。有効にするには、`xhr.withCredentials` を `true` にします: + +```js +let xhr = new XMLHttpRequest(); +*!* +xhr.withCredentials = true; +*/!* + +xhr.open('POST', 'http://anywhere.com/request'); +... +``` + +クロスオリジンヘッダに関しての詳細はチャプター <info:fetch-crossorigin> を参照してください。 + +## サマリ + +`XMLHttpRequest` を使用した GET リクエストの典型的なコード: + +```js +let xhr = new XMLHttpRequest(); + +xhr.open('GET', '/my/url'); + +xhr.send(); + +xhr.onload = function() { + if (xhr.status != 200) { // HTTP error? + // エラー処理 + alert( 'Error: ' + xhr.status); + return; + } + + // xhr.response でレスポンス取得 +}; + +xhr.onprogress = function(event) { + // 進行状況の報告 + alert(`Loaded ${event.loaded} of ${event.total}`); +}; + +xhr.onerror = function() { + // 非 HTTP エラーの処理(e.g. ネットワークダウン) +}; +``` + +実施にはより多くのイベントがあり、[現在の仕様](http://www.w3.org/TR/XMLHttpRequest/#events) でリストされています(ライフサイクル順): + +- `loadstart` -- リクエストが開始された +- `progress` -- レスポンスのデータパケットが到着し、その時点のレスポンス本文全体は `responseText` にあります +- `abort` -- リクエストが `xhr.abort()` 呼び出しによりキャンセルされた +- `error` -- 接続エラーが発生。e.g. 間違ったドメイン名など. 404 などのHTTPエラーでは発生しません。 +- `load` -- リクエストが正常に終了した +- `timeout` -- タイムアウトでリクエストがキャンセルされた(タイムアウトが設定された場合のみ)). +- `loadend` -- `load`, `error`, `timeout` or `abort` の後に発生します。. + +`error`, `abort`, `timeout`, と `load` イベントは相互に排他的です。それらの1つだけが発生します。 + +最も使われているイベントはロード完了 (`load`), ロード失敗(`error`)です。あるいは、単一の `loadend` ハンドラを使用して何が起こったのかを確認するためにレスポンスをチェックします。 + +すでに別のイベント `readystatechange` を見てきました。歴史的には、仕様が定まるずっと前からありました。最近では、これを使う必要はありません。新しいイベントに置き換えることができますが、多くの場合、古いスクリプトにあります。 + +特にアップロードを追跡する必要がある場合は、`xhr.upload` オブジェクトで同じイベントをリッスンする必要があります。 diff --git a/5-network/08-xmlhttprequest/example.view/index.html b/5-network/08-xmlhttprequest/example.view/index.html new file mode 100644 index 0000000000..7dbc3d9510 --- /dev/null +++ b/5-network/08-xmlhttprequest/example.view/index.html @@ -0,0 +1,30 @@ +<!DOCTYPE HTML> +<script> +function run() { + + let xhr = new XMLHttpRequest(); + write(`readyState=${xhr.readyState}`); + + xhr.open('GET', 'digits'); + write(`readyState=${xhr.readyState}`); + + xhr.onreadystatechange = function() { + write(`readyState=${xhr.readyState}, responseText.length=${xhr.responseText.length}`); + }; + + xhr.onprogress = function() { + write(`readyState=${xhr.readyState}, responseText.length=${xhr.responseText.length}`); + }; + + xhr.send(); +} + +function write(text) { + let li = log.appendChild(document.createElement('li')); + li.innerHTML = text; +} +</script> + +<button onclick="run()">Load digits</button> + +<ul id="log"></ul> diff --git a/5-network/08-xmlhttprequest/example.view/server.js b/5-network/08-xmlhttprequest/example.view/server.js new file mode 100644 index 0000000000..a387293b90 --- /dev/null +++ b/5-network/08-xmlhttprequest/example.view/server.js @@ -0,0 +1,52 @@ +let http = require('http'); +let url = require('url'); +let querystring = require('querystring'); +let static = require('node-static'); +let file = new static.Server('.'); + +function accept(req, res) { + + if (req.url == '/load') { + + res.writeHead(200, { + 'Content-Type': 'text/plain', + 'Cache-Control': 'no-cache', + 'Content-Length': 90000 + }); + + let i = 0; + + let timer = setInterval(write, 1000); + write(); + + function write() { + res.write(String(i).repeat(10000)); + i++; + if (i == 9) { + clearInterval(timer); + res.end(); + } + + } + } else if (req.url == '/json') { + res.writeHead(200, { + // 'Content-Type': 'application/json;charset=utf-8', + 'Cache-Control': 'no-cache' + }); + + res.write(JSON.stringify({message: "Hello, world!"})); + res.end(); + } else { + file.serve(req, res); + } +} + + + +// ----- запуск accept как сервера из консоли или как модуля ------ + +if (!module.parent) { + http.createServer(accept).listen(8080); +} else { + exports.accept = accept; +} diff --git a/7-network/1-xmlhttprequest/hello.txt b/5-network/08-xmlhttprequest/hello.txt similarity index 100% rename from 7-network/1-xmlhttprequest/hello.txt rename to 5-network/08-xmlhttprequest/hello.txt diff --git a/5-network/08-xmlhttprequest/phones-async.view/index.html b/5-network/08-xmlhttprequest/phones-async.view/index.html new file mode 100644 index 0000000000..dff72ba3dc --- /dev/null +++ b/5-network/08-xmlhttprequest/phones-async.view/index.html @@ -0,0 +1,42 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> +</head> +<body> + + <button onclick="loadPhones()" id="button">Load phones.json!</button> + + <script> + function loadPhones() { + + let xhr = new XMLHttpRequest(); + + xhr.open('GET', 'phones.json'); + + + xhr.send(); + + + xhr.onreadystatechange = function() { + if (xhr.readyState != 4) return; + + button.innerHTML = 'Complete!'; + + if (xhr.status != 200) { + // handle error + alert(xhr.status + ': ' + xhr.statusText); + } else { + // show result + alert(xhr.responseText); + } + + } + + button.innerHTML = 'Loading...'; + button.disabled = true; + } + </script> + +</body> +</html> diff --git a/7-network/1-xmlhttprequest/phones.json b/5-network/08-xmlhttprequest/phones-async.view/phones.json old mode 100755 new mode 100644 similarity index 100% rename from 7-network/1-xmlhttprequest/phones.json rename to 5-network/08-xmlhttprequest/phones-async.view/phones.json diff --git a/5-network/08-xmlhttprequest/phones-async.view/server.js b/5-network/08-xmlhttprequest/phones-async.view/server.js new file mode 100644 index 0000000000..d09318a007 --- /dev/null +++ b/5-network/08-xmlhttprequest/phones-async.view/server.js @@ -0,0 +1,30 @@ +var http = require('http'); +var url = require('url'); +var querystring = require('querystring'); +var static = require('node-static'); +var file = new static.Server('.', { + cache: 0 +}); + + +function accept(req, res) { + + if (req.url == '/phones.json') { + // искусственная задержка для наглядности + setTimeout(function() { + file.serve(req, res); + }, 2000); + } else { + file.serve(req, res); + } + +} + + +// ------ запустить сервер ------- + +if (!module.parent) { + http.createServer(accept).listen(8080); +} else { + exports.accept = accept; +} \ No newline at end of file diff --git a/5-network/08-xmlhttprequest/phones.json b/5-network/08-xmlhttprequest/phones.json new file mode 100644 index 0000000000..a224f59e52 --- /dev/null +++ b/5-network/08-xmlhttprequest/phones.json @@ -0,0 +1,155 @@ +[ + { + "age": 0, + "id": "motorola-xoom-with-wi-fi", + "imageUrl": "img/phones/motorola-xoom-with-wi-fi.0.jpg", + "name": "Motorola XOOM\u2122 with Wi-Fi", + "snippet": "The Next, Next Generation\r\n\r\nExperience the future with Motorola XOOM with Wi-Fi, the world's first tablet powered by Android 3.0 (Honeycomb)." + }, + { + "age": 1, + "id": "motorola-xoom", + "imageUrl": "img/phones/motorola-xoom.0.jpg", + "name": "MOTOROLA XOOM\u2122", + "snippet": "The Next, Next Generation\n\nExperience the future with MOTOROLA XOOM, the world's first tablet powered by Android 3.0 (Honeycomb)." + }, + { + "age": 2, + "carrier": "AT&T", + "id": "motorola-atrix-4g", + "imageUrl": "img/phones/motorola-atrix-4g.0.jpg", + "name": "MOTOROLA ATRIX\u2122 4G", + "snippet": "MOTOROLA ATRIX 4G the world's most powerful smartphone." + }, + { + "age": 3, + "id": "dell-streak-7", + "imageUrl": "img/phones/dell-streak-7.0.jpg", + "name": "Dell Streak 7", + "snippet": "Introducing Dell\u2122 Streak 7. Share photos, videos and movies together. It\u2019s small enough to carry around, big enough to gather around." + }, + { + "age": 4, + "carrier": "Cellular South", + "id": "samsung-gem", + "imageUrl": "img/phones/samsung-gem.0.jpg", + "name": "Samsung Gem\u2122", + "snippet": "The Samsung Gem\u2122 brings you everything that you would expect and more from a touch display smart phone \u2013 more apps, more features and a more affordable price." + }, + { + "age": 5, + "carrier": "Dell", + "id": "dell-venue", + "imageUrl": "img/phones/dell-venue.0.jpg", + "name": "Dell Venue", + "snippet": "The Dell Venue; Your Personal Express Lane to Everything" + }, + { + "age": 6, + "carrier": "Best Buy", + "id": "nexus-s", + "imageUrl": "img/phones/nexus-s.0.jpg", + "name": "Nexus S", + "snippet": "Fast just got faster with Nexus S. A pure Google experience, Nexus S is the first phone to run Gingerbread (Android 2.3), the fastest version of Android yet." + }, + { + "age": 7, + "carrier": "Cellular South", + "id": "lg-axis", + "imageUrl": "img/phones/lg-axis.0.jpg", + "name": "LG Axis", + "snippet": "Android Powered, Google Maps Navigation, 5 Customizable Home Screens" + }, + { + "age": 8, + "id": "samsung-galaxy-tab", + "imageUrl": "img/phones/samsung-galaxy-tab.0.jpg", + "name": "Samsung Galaxy Tab\u2122", + "snippet": "Feel Free to Tab\u2122. The Samsung Galaxy Tab\u2122 brings you an ultra-mobile entertainment experience through its 7\u201d display, high-power processor and Adobe\u00ae Flash\u00ae Player compatibility." + }, + { + "age": 9, + "carrier": "Cellular South", + "id": "samsung-showcase-a-galaxy-s-phone", + "imageUrl": "img/phones/samsung-showcase-a-galaxy-s-phone.0.jpg", + "name": "Samsung Showcase\u2122 a Galaxy S\u2122 phone", + "snippet": "The Samsung Showcase\u2122 delivers a cinema quality experience like you\u2019ve never seen before. Its innovative 4\u201d touch display technology provides rich picture brilliance, even outdoors" + }, + { + "age": 10, + "carrier": "Verizon", + "id": "droid-2-global-by-motorola", + "imageUrl": "img/phones/droid-2-global-by-motorola.0.jpg", + "name": "DROID\u2122 2 Global by Motorola", + "snippet": "The first smartphone with a 1.2 GHz processor and global capabilities." + }, + { + "age": 11, + "carrier": "Verizon", + "id": "droid-pro-by-motorola", + "imageUrl": "img/phones/droid-pro-by-motorola.0.jpg", + "name": "DROID\u2122 Pro by Motorola", + "snippet": "The next generation of DOES." + }, + { + "age": 12, + "carrier": "AT&T", + "id": "motorola-bravo-with-motoblur", + "imageUrl": "img/phones/motorola-bravo-with-motoblur.0.jpg", + "name": "MOTOROLA BRAVO\u2122 with MOTOBLUR\u2122", + "snippet": "An experience to cheer about." + }, + { + "age": 13, + "carrier": "T-Mobile", + "id": "motorola-defy-with-motoblur", + "imageUrl": "img/phones/motorola-defy-with-motoblur.0.jpg", + "name": "Motorola DEFY\u2122 with MOTOBLUR\u2122", + "snippet": "Are you ready for everything life throws your way?" + }, + { + "age": 14, + "carrier": "T-Mobile", + "id": "t-mobile-mytouch-4g", + "imageUrl": "img/phones/t-mobile-mytouch-4g.0.jpg", + "name": "T-Mobile myTouch 4G", + "snippet": "The T-Mobile myTouch 4G is a premium smartphone designed to deliver blazing fast 4G speeds so that you can video chat from practically anywhere, with or without Wi-Fi." + }, + { + "age": 15, + "carrier": "US Cellular", + "id": "samsung-mesmerize-a-galaxy-s-phone", + "imageUrl": "img/phones/samsung-mesmerize-a-galaxy-s-phone.0.jpg", + "name": "Samsung Mesmerize\u2122 a Galaxy S\u2122 phone", + "snippet": "The Samsung Mesmerize\u2122 delivers a cinema quality experience like you\u2019ve never seen before. Its innovative 4\u201d touch display technology provides rich picture brilliance,even outdoors" + }, + { + "age": 16, + "carrier": "Sprint", + "id": "sanyo-zio", + "imageUrl": "img/phones/sanyo-zio.0.jpg", + "name": "SANYO ZIO", + "snippet": "The Sanyo Zio by Kyocera is an Android smartphone with a combination of ultra-sleek styling, strong performance and unprecedented value." + }, + { + "age": 17, + "id": "samsung-transform", + "imageUrl": "img/phones/samsung-transform.0.jpg", + "name": "Samsung Transform\u2122", + "snippet": "The Samsung Transform\u2122 brings you a fun way to customize your Android powered touch screen phone to just the way you like it through your favorite themed \u201cSprint ID Service Pack\u201d." + }, + { + "age": 18, + "id": "t-mobile-g2", + "imageUrl": "img/phones/t-mobile-g2.0.jpg", + "name": "T-Mobile G2", + "snippet": "The T-Mobile G2 with Google is the first smartphone built for 4G speeds on T-Mobile's new network. Get the information you need, faster than you ever thought possible." + }, + { + "age": 19, + "id": "motorola-charm-with-motoblur", + "imageUrl": "img/phones/motorola-charm-with-motoblur.0.jpg", + "name": "Motorola CHARM\u2122 with MOTOBLUR\u2122", + "snippet": "Motorola CHARM fits easily in your pocket or palm. Includes MOTOBLUR service." + } +] diff --git a/5-network/08-xmlhttprequest/phones.view/index.html b/5-network/08-xmlhttprequest/phones.view/index.html new file mode 100644 index 0000000000..02e1fb9674 --- /dev/null +++ b/5-network/08-xmlhttprequest/phones.view/index.html @@ -0,0 +1,28 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> +</head> +<body> + + <button onclick="loadPhones()">Load phones.json!</button> + + <script> + function loadPhones() { + let xhr = new XMLHttpRequest(); + + xhr.open('GET', 'phones.json', false); + xhr.send(); + + if (xhr.status != 200) { + // handle error + alert('Error ' + xhr.status + ': ' + xhr.statusText); + } else { + // show result + alert(xhr.responseText); + } + } + </script> + +</body> +</html> diff --git a/5-network/08-xmlhttprequest/phones.view/phones.json b/5-network/08-xmlhttprequest/phones.view/phones.json new file mode 100644 index 0000000000..472b2b169f --- /dev/null +++ b/5-network/08-xmlhttprequest/phones.view/phones.json @@ -0,0 +1,155 @@ +[ + { + "age": 0, + "id": "motorola-xoom-with-wi-fi", + "imageUrl": "img/phones/motorola-xoom-with-wi-fi.0.jpg", + "name": "Motorola XOOM\u2122 with Wi-Fi", + "snippet": "The Next, Next Generation\r\n\r\nExperience the future with Motorola XOOM with Wi-Fi, the world's first tablet powered by Android 3.0 (Honeycomb)." + }, + { + "age": 1, + "id": "motorola-xoom", + "imageUrl": "img/phones/motorola-xoom.0.jpg", + "name": "MOTOROLA XOOM\u2122", + "snippet": "The Next, Next Generation\n\nExperience the future with MOTOROLA XOOM, the world's first tablet powered by Android 3.0 (Honeycomb)." + }, + { + "age": 2, + "carrier": "AT&T", + "id": "motorola-atrix-4g", + "imageUrl": "img/phones/motorola-atrix-4g.0.jpg", + "name": "MOTOROLA ATRIX\u2122 4G", + "snippet": "MOTOROLA ATRIX 4G the world's most powerful smartphone." + }, + { + "age": 3, + "id": "dell-streak-7", + "imageUrl": "img/phones/dell-streak-7.0.jpg", + "name": "Dell Streak 7", + "snippet": "Introducing Dell\u2122 Streak 7. Share photos, videos and movies together. It\u2019s small enough to carry around, big enough to gather around." + }, + { + "age": 4, + "carrier": "Cellular South", + "id": "samsung-gem", + "imageUrl": "img/phones/samsung-gem.0.jpg", + "name": "Samsung Gem\u2122", + "snippet": "The Samsung Gem\u2122 brings you everything that you would expect and more from a touch display smart phone \u2013 more apps, more features and a more affordable price." + }, + { + "age": 5, + "carrier": "Dell", + "id": "dell-venue", + "imageUrl": "img/phones/dell-venue.0.jpg", + "name": "Dell Venue", + "snippet": "The Dell Venue; Your Personal Express Lane to Everything" + }, + { + "age": 6, + "carrier": "Best Buy", + "id": "nexus-s", + "imageUrl": "img/phones/nexus-s.0.jpg", + "name": "Nexus S", + "snippet": "Fast just got faster with Nexus S. A pure Google experience, Nexus S is the first phone to run Gingerbread (Android 2.3), the fastest version of Android yet." + }, + { + "age": 7, + "carrier": "Cellular South", + "id": "lg-axis", + "imageUrl": "img/phones/lg-axis.0.jpg", + "name": "LG Axis", + "snippet": "Android Powered, Google Maps Navigation, 5 Customizable Home Screens" + }, + { + "age": 8, + "id": "samsung-galaxy-tab", + "imageUrl": "img/phones/samsung-galaxy-tab.0.jpg", + "name": "Samsung Galaxy Tab\u2122", + "snippet": "Feel Free to Tab\u2122. The Samsung Galaxy Tab\u2122 brings you an ultra-mobile entertainment experience through its 7\u201d display, high-power processor and Adobe\u00ae Flash\u00ae Player compatibility." + }, + { + "age": 9, + "carrier": "Cellular South", + "id": "samsung-showcase-a-galaxy-s-phone", + "imageUrl": "img/phones/samsung-showcase-a-galaxy-s-phone.0.jpg", + "name": "Samsung Showcase\u2122 a Galaxy S\u2122 phone", + "snippet": "The Samsung Showcase\u2122 delivers a cinema quality experience like you\u2019ve never seen before. Its innovative 4\u201d touch display technology provides rich picture brilliance, even outdoors" + }, + { + "age": 10, + "carrier": "Verizon", + "id": "droid-2-global-by-motorola", + "imageUrl": "img/phones/droid-2-global-by-motorola.0.jpg", + "name": "DROID\u2122 2 Global by Motorola", + "snippet": "The first smartphone with a 1.2 GHz processor and global capabilities." + }, + { + "age": 11, + "carrier": "Verizon", + "id": "droid-pro-by-motorola", + "imageUrl": "img/phones/droid-pro-by-motorola.0.jpg", + "name": "DROID\u2122 Pro by Motorola", + "snippet": "The next generation of DOES." + }, + { + "age": 12, + "carrier": "AT&T", + "id": "motorola-bravo-with-motoblur", + "imageUrl": "img/phones/motorola-bravo-with-motoblur.0.jpg", + "name": "MOTOROLA BRAVO\u2122 with MOTOBLUR\u2122", + "snippet": "An experience to cheer about." + }, + { + "age": 13, + "carrier": "T-Mobile", + "id": "motorola-defy-with-motoblur", + "imageUrl": "img/phones/motorola-defy-with-motoblur.0.jpg", + "name": "Motorola DEFY\u2122 with MOTOBLUR\u2122", + "snippet": "Are you ready for everything life throws your way?" + }, + { + "age": 14, + "carrier": "T-Mobile", + "id": "t-mobile-mytouch-4g", + "imageUrl": "img/phones/t-mobile-mytouch-4g.0.jpg", + "name": "T-Mobile myTouch 4G", + "snippet": "The T-Mobile myTouch 4G is a premium smartphone designed to deliver blazing fast 4G speeds so that you can video chat from practically anywhere, with or without Wi-Fi." + }, + { + "age": 15, + "carrier": "US Cellular", + "id": "samsung-mesmerize-a-galaxy-s-phone", + "imageUrl": "img/phones/samsung-mesmerize-a-galaxy-s-phone.0.jpg", + "name": "Samsung Mesmerize\u2122 a Galaxy S\u2122 phone", + "snippet": "The Samsung Mesmerize\u2122 delivers a cinema quality experience like you\u2019ve never seen before. Its innovative 4\u201d touch display technology provides rich picture brilliance,even outdoors" + }, + { + "age": 16, + "carrier": "Sprint", + "id": "sanyo-zio", + "imageUrl": "img/phones/sanyo-zio.0.jpg", + "name": "SANYO ZIO", + "snippet": "The Sanyo Zio by Kyocera is an Android smartphone with a combination of ultra-sleek styling, strong performance and unprecedented value." + }, + { + "age": 17, + "id": "samsung-transform", + "imageUrl": "img/phones/samsung-transform.0.jpg", + "name": "Samsung Transform\u2122", + "snippet": "The Samsung Transform\u2122 brings you a fun way to customize your Android powered touch screen phone to just the way you like it through your favorite themed \u201cSprint ID Service Pack\u201d." + }, + { + "age": 18, + "id": "t-mobile-g2", + "imageUrl": "img/phones/t-mobile-g2.0.jpg", + "name": "T-Mobile G2", + "snippet": "The T-Mobile G2 with Google is the first smartphone built for 4G speeds on T-Mobile's new network. Get the information you need, faster than you ever thought possible." + }, + { + "age": 19, + "id": "motorola-charm-with-motoblur", + "imageUrl": "img/phones/motorola-charm-with-motoblur.0.jpg", + "name": "Motorola CHARM\u2122 with MOTOBLUR\u2122", + "snippet": "Motorola CHARM fits easily in your pocket or palm. Includes MOTOBLUR service." + } +] diff --git a/5-network/08-xmlhttprequest/phones.view/server.js b/5-network/08-xmlhttprequest/phones.view/server.js new file mode 100644 index 0000000000..d8bf907853 --- /dev/null +++ b/5-network/08-xmlhttprequest/phones.view/server.js @@ -0,0 +1,30 @@ +let http = require('http'); +let url = require('url'); +let querystring = require('querystring'); +let static = require('node-static'); +let file = new static.Server('.', { + cache: 0 +}); + + +function accept(req, res) { + + if (req.url == '/phones.json') { + // stall a bit to let "loading" message show up + setTimeout(function() { + file.serve(req, res); + }, 2000); + } else { + file.serve(req, res); + } + +} + + +// ------ запустить сервер ------- + +if (!module.parent) { + http.createServer(accept).listen(8080); +} else { + exports.accept = accept; +} diff --git a/5-network/08-xmlhttprequest/post.view/index.html b/5-network/08-xmlhttprequest/post.view/index.html new file mode 100644 index 0000000000..ba7f76065e --- /dev/null +++ b/5-network/08-xmlhttprequest/post.view/index.html @@ -0,0 +1,36 @@ +<!doctype html> +<script> +(async () { + + const response = await fetch('long.txt'); + const reader = response.body.getReader(); + + const contentLength = +response.headers.get('Content-Length'); + let receivedLength = 0; + let chunks = []; + while(true) { + const chunk = await reader.read(); + + if (chunk.done) { + console.log("done!"); + break; + } + + chunks.push(chunk.value); + receivedLength += chunk.value.length; + console.log(`${receivedLength}/${contentLength} received`) + } + + + let chunksMerged = new Uint8Array(receivedLength); + let length = 0; + for(let chunk of chunks) { + chunksMerged.set(chunk, length); + length += chunk.length; + } + + let result = new TextDecoder("utf-8").decode(chunksMerged); + console.log(result); +})(); + +</script> diff --git a/5-network/08-xmlhttprequest/post.view/server.js b/5-network/08-xmlhttprequest/post.view/server.js new file mode 100644 index 0000000000..50cfc5ae36 --- /dev/null +++ b/5-network/08-xmlhttprequest/post.view/server.js @@ -0,0 +1,58 @@ +let http = require('http'); +let url = require('url'); +let querystring = require('querystring'); +let static = require('node-static'); +let file = new static.Server('.', { + cache: 0 +}); + + +function accept(req, res) { + + if (req.method == 'POST') { + let chunks = []; + let length = 0; + + req.on('data', function (data) { + chunks.push(data); + length += data.length; + + // More than 10mb, kill the connection! + if (length > 1e8) { + req.connection.destroy(); + } + }); + + req.on('end', function() { + // let post = JSON.parse(chunks.join('')); + + if (req.url == '/user') { + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ message: 'User saved' })); + } else if (req.url == '/image') { + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ message: "Image saved", imageSize: length })); + } else if (req.url == '/upload') { + res.writeHead(200, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ message: "Upload complete", size: length })); + } else { + res.writeHead(404); + res.end("Not found"); + } + }); + + + } else { + file.serve(req, res); + } + +} + + +// ------ запустить сервер ------- + +if (!module.parent) { + http.createServer(accept).listen(8080); +} else { + exports.accept = accept; +} diff --git a/5-network/09-resume-upload/article.md b/5-network/09-resume-upload/article.md new file mode 100644 index 0000000000..3d425619ab --- /dev/null +++ b/5-network/09-resume-upload/article.md @@ -0,0 +1,73 @@ +# 再開可能なファイルアップロード + +`fetch` メソッドを使用すると、とても簡単にファイルをアップロードすることができます。 + +接続が切れた後、アップロードを再開するにはどうすればよいでしょう?そのための組み込みのオプションはありません。が、ここではそれを実装するためのヒントがあります。 + +再開可能なアップロードには、アップロードの進行状況の表示が一緒に必要になります。大きなファイルが期待されるからです(再開が必要かもしれないほどのサイズ)。したがって、 `fetch` ではアップロードの進行状況を追跡できないため、[XMLHttpRequest](info:xmlhttprequest)を使います。 + +## あまり役に立たない progress イベント + +アップロードを再開するには、接続が失われるまでにどれだけアップロードされたかを知る必要があります。 + +アップロードの進行状況を追跡する `xhr.upload.onprogress` があります。 + +ただ、残念ながらここでは役に立ちません。これはデータが *送られた* ときに発生しますが、サーバが受け取ったかはブラウザは知らないからです。 + +ローカルネットワークプロキシによってバッファリングされていたり、リモートサーバプロセスが停止して処理ができなかったり、あるいは接続が切れた際に途中で失われサーバに届かなったかもしれません。 + +したがって、このイベントはプログレスバーを出すときにだけ役立ちます。 + +アップロードを再開するには、サーバが受け取った正確なバイト数を知る必要があります。そして、それを知っているのはサーバだけです。 + +## アルゴリズム + +1. 最初に、アップロードするファイルを一意に識別するために、ファイル id を作成します。e.g. + ```js + let fileId = file.name + '-' + file.size + '-' + +file.lastModifiedDate; + ``` + これはアップロードを再開するのに必要で、サーバに再開する内容を伝えるときに使います。 + +2. サーバにリクエストを送り、すでに受け取り済みのバイト数を訪ねます。次のようになります: + ```js + let response = await fetch('status', { + headers: { + 'X-File-Id': fileId + } + }); + + // The server has that many bytes + let startByte = +await response.text(); + ``` + + これは、サーバがファイルのアップロードを `X-File-Id` ヘッダで追跡していることを前提としています。サーバ側で実装されている必要があります。 + +3. 次に, `Blob` のメソッド `slice` を使って `startByte` からファイルを送信します。: + ```js + xhr.open("POST", "upload", true); + + // サーバが再開されるファイルをしるためにファイル id を送信します + xhr.setRequestHeader('X-File-Id', fileId); + // 再開時のバイトを送信します + xhr.setRequestHeader('X-Start-Byte', startByte); + + xhr.upload.onprogress = (e) => { + console.log(`Uploaded ${startByte + e.loaded} of ${startByte + e.total}`); + }; + + // ファイルは input.files[0] または別の元から取得できます + xhr.send(file.slice(startByte)); + ``` + + ここでは、サーバに両方のファイル id を `X-File-Id` として送信するため、どのファイルをアップロードしているかが分かります。そして `X-Start-Byte` で開始バイトを送信するので、最初にアップロードするのではなく、再開であることを認識します。 + + サーバはそのレコードをチェックし、そのファイルのアップロードがあり、かつ現在のアップロードサイズが正確に `X-Start-Byte` であれば、データを追加します。 + + +これはクライアントとサーバ両方のコードを使用したデモです。Node.js で記述しています。 + +Node.js は Nginx という別のサーバの裏におり、これがアップロードをバッファリングし、完全に完了したときに Node.js にわたすため、このサイト上では、部分的にしか機能しません。 + +ですが、ダウンロードして、ローカルで完全なデモを見ることもできます: + +[codetabs src="upload-resume" height=200] diff --git a/5-network/09-resume-upload/upload-resume.view/index.html b/5-network/09-resume-upload/upload-resume.view/index.html new file mode 100644 index 0000000000..f1178145e2 --- /dev/null +++ b/5-network/09-resume-upload/upload-resume.view/index.html @@ -0,0 +1,50 @@ +<!DOCTYPE HTML> + +<script src="uploader.js"></script> + +<form name="upload" method="POST" enctype="multipart/form-data" action="/upload"> + <input type="file" name="myfile"> + <input type="submit" name="submit" value="Upload (Resumes automatically)"> +</form> + +<button onclick="uploader.stop()">Stop upload</button> + + +<div id="log">Progress indication</div> + +<script> + function log(html) { + document.getElementById('log').innerHTML = html; + console.log(html); + } + + function onProgress(loaded, total) { + log("progress " + loaded + ' / ' + total); + } + + let uploader; + + document.forms.upload.onsubmit = async function(e) { + e.preventDefault(); + + let file = this.elements.myfile.files[0]; + if (!file) return; + + uploader = new Uploader({file, onProgress}); + + try { + let uploaded = await uploader.upload(); + + if (uploaded) { + log('success'); + } else { + log('stopped'); + } + + } catch(err) { + console.error(err); + log('error'); + } + }; + +</script> diff --git a/5-network/09-resume-upload/upload-resume.view/server.js b/5-network/09-resume-upload/upload-resume.view/server.js new file mode 100644 index 0000000000..41f3979703 --- /dev/null +++ b/5-network/09-resume-upload/upload-resume.view/server.js @@ -0,0 +1,123 @@ +let http = require('http'); +let static = require('node-static'); +let fileServer = new static.Server('.'); +let path = require('path'); +let fs = require('fs'); +let debug = require('debug')('example:resume-upload'); + +let uploads = Object.create(null); + +function onUpload(req, res) { + + let fileId = req.headers['x-file-id']; + let startByte = req.headers['x-start-byte']; + + if (!fileId) { + res.writeHead(400, "No file id"); + res.end(); + } + + // we'll files "nowhere" + let filePath = '/dev/null'; + // could use a real path instead, e.g. + // let filePath = path.join('/tmp', fileId); + + debug("onUpload fileId: ", fileId); + + // initialize a new upload + if (!uploads[fileId]) uploads[fileId] = {}; + let upload = uploads[fileId]; + + debug("bytesReceived:" + upload.bytesReceived + " startByte:" + startByte) + + let fileStream; + + // if startByte is 0 or not set, create a new file, otherwise check the size and append to existing one + if (!startByte) { + upload.bytesReceived = 0; + fileStream = fs.createWriteStream(filePath, { + flags: 'w' + }); + debug("New file created: " + filePath); + } else { + // we can check on-disk file size as well to be sure + if (upload.bytesReceived != startByte) { + res.writeHead(400, "Wrong start byte"); + res.end(upload.bytesReceived); + return; + } + // append to existing file + fileStream = fs.createWriteStream(filePath, { + flags: 'a' + }); + debug("File reopened: " + filePath); + } + + + req.on('data', function(data) { + debug("bytes received", upload.bytesReceived); + upload.bytesReceived += data.length; + }); + + // send request body to file + req.pipe(fileStream); + + // when the request is finished, and all its data is written + fileStream.on('close', function() { + if (upload.bytesReceived == req.headers['x-file-size']) { + debug("Upload finished"); + delete uploads[fileId]; + + // can do something else with the uploaded file here + + res.end("Success " + upload.bytesReceived); + } else { + // connection lost, we leave the unfinished file around + debug("File unfinished, stopped at " + upload.bytesReceived); + res.end(); + } + }); + + // in case of I/O error - finish the request + fileStream.on('error', function(err) { + debug("fileStream error"); + res.writeHead(500, "File error"); + res.end(); + }); + +} + +function onStatus(req, res) { + let fileId = req.headers['x-file-id']; + let upload = uploads[fileId]; + debug("onStatus fileId:", fileId, " upload:", upload); + if (!upload) { + res.end("0") + } else { + res.end(String(upload.bytesReceived)); + } +} + + +function accept(req, res) { + if (req.url == '/status') { + onStatus(req, res); + } else if (req.url == '/upload' && req.method == 'POST') { + onUpload(req, res); + } else { + fileServer.serve(req, res); + } + +} + + + + +// ----------------------------------- + +if (!module.parent) { + http.createServer(accept).listen(8080); + console.log('Server listening at port 8080'); +} else { + exports.accept = accept; +} diff --git a/5-network/09-resume-upload/upload-resume.view/uploader.js b/5-network/09-resume-upload/upload-resume.view/uploader.js new file mode 100644 index 0000000000..2e53ce4a31 --- /dev/null +++ b/5-network/09-resume-upload/upload-resume.view/uploader.js @@ -0,0 +1,75 @@ +class Uploader { + + constructor({file, onProgress}) { + this.file = file; + this.onProgress = onProgress; + + // create fileId that uniquely identifies the file + // we could also add user session identifier (if had one), to make it even more unique + this.fileId = file.name + '-' + file.size + '-' + +file.lastModifiedDate; + } + + async getUploadedBytes() { + let response = await fetch('status', { + headers: { + 'X-File-Id': this.fileId + } + }); + + if (response.status != 200) { + throw new Error("Can't get uploaded bytes: " + response.statusText); + } + + let text = await response.text(); + + return +text; + } + + async upload() { + this.startByte = await this.getUploadedBytes(); + + let xhr = this.xhr = new XMLHttpRequest(); + xhr.open("POST", "upload", true); + + // send file id, so that the server knows which file to resume + xhr.setRequestHeader('X-File-Id', this.fileId); + // send the byte we're resuming from, so the server knows we're resuming + xhr.setRequestHeader('X-Start-Byte', this.startByte); + + xhr.upload.onprogress = (e) => { + this.onProgress(this.startByte + e.loaded, this.startByte + e.total); + }; + + console.log("send the file, starting from", this.startByte); + xhr.send(this.file.slice(this.startByte)); + + // return + // true if upload was successful, + // false if aborted + // throw in case of an error + return await new Promise((resolve, reject) => { + + xhr.onload = xhr.onerror = () => { + console.log("upload end status:" + xhr.status + " text:" + xhr.statusText); + + if (xhr.status == 200) { + resolve(true); + } else { + reject(new Error("Upload failed: " + xhr.statusText)); + } + }; + + // onabort triggers only when xhr.abort() is called + xhr.onabort = () => resolve(false); + + }); + + } + + stop() { + if (this.xhr) { + this.xhr.abort(); + } + } + +} diff --git a/5-network/10-long-polling/article.md b/5-network/10-long-polling/article.md new file mode 100644 index 0000000000..1c1387fd71 --- /dev/null +++ b/5-network/10-long-polling/article.md @@ -0,0 +1,94 @@ +# ロングポーリング + +ロングポーリング(Long polling)はサーバと永続的な接続を持つための最も簡単な方法で、WebSocket や Server Side Events などの特定のプロトコルを使いません。 + +実装はとても簡単であり、多くのケースもこれで十分です。 + +## 定期的なポーリング + +サーバから新しい情報を取得するための最も簡単な方法は、ポーリングです。 + +つまり、サーバへの定期的なリクエストです: "こんにちは、私はここにいます。私に関してなにか新しい情報はありますか?"。例えば、10秒毎に。 + +応答では、サーバは最初にクライアントがオンラインであること自身に知らせて、次にその時までに受け取ったメッセージのパケットを送信します。 + +これは機能しますが、デメリットもあります。: +1. メッセージは最大10秒(リクエスト間隔)の遅延が発生します +2. たとえメッセージがない場合でも、サーバは10秒ごとにリクエストを受け取ります。パフォーマンス観点から見えると、これはバックエンドの処理にかなりの負荷がかかります。 + +そのため、非常に小さいサービスに関して話す場合にはこのアプローチは現実的ですが、一般的には改善が必要です。 + +## ロングポーリング + +いわゆる "ロングポーリング" はサーバにポーリングするための、はるかに優れた方法です。 + +これも実装はとても簡単で、遅延なしでメッセージを配信します。 + +フローです: + +1. リクエストがサーバに送信されます。 +2. サーバはメッセージがあるまで接続を閉じません。 +3. メッセージが現れたら、サーバはそのデータでリクエストに応答します。 +4. ブラウザはすぐに新しいリクエストを作ります。 + +ブラウザがリクエストを送信し、サーバとの接続を保留にしている状況は、このメソッドの普通の状態です。メッセージが配信されたときだけ、接続が再確立されます。 + +![](long-polling.svg) + +もしもネットワークエラーなどで接続が失われた場合は、ブラウザはすぐに新しいリクエストを送信します。 + +長いリクエストを行う、クライアント側の `subscribe` 関数の概要です。: + +```js +async function subscribe() { + let response = await fetch("/subscribe"); + + if (response.status == 502) { + // 接続タイムアウトエラー + // 接続が長時間保留されていて、リモートサーバやプロキシがそれを閉じたときに発生する場合があります + // 再接続しましょう + await subscribe(); + } else if (response.status != 200) { + // エラーを表示 + showMessage(response.statusText); + // 1秒後に再接続します + await new Promise(resolve => setTimeout(resolve, 1000)); + await subscribe(); + } else { + // メッセージを取得しました + let message = await response.text(); + showMessage(message); + await subscribe(); + } +} + +subscribe(); +``` + +ご覧の通り、`subscribe` 関数は fetch を生成し、応答を待ってから処理を行い、その後再び自身を呼び出します。 + +```warn header="サーバは多数の保留中の接続でもOKである必要があります" +サーバのアーキテクチャは、多数の保留中の接続があっても問題なく動作できるようにする必要があります。 + +特定のサーバアーキテクチャは、接続毎にプロセスを実行します。多数の接続がある場合、プロセスも多数になり、各プロセスが大量のメモリを消費します。 + +これは、しばしばバックエンドが PHP, Ruby で書かれたバックエンドの場合ですが、技術的には言語ではなく実装の問題です。 + +Node.js で書かれたバックエンドには、通常このような問題は起こりません。 +``` + +## デモ: チャット + +デモです: + +[codetabs src="longpoll" height=500] + +## Area of usage + +ロングポーリングはメッセージがあまり来ないような状況で上手く機能します。 + +もしメッセージが非常に頻繁にくる場合、メッセージの送受信の図は上で描かれたような、のこぎりのようになります。 + +すべてのメッセージは個別のリクエストであり、それぞれがヘッダや認証のオーバヘッドなどを持ちます。 + +そのため、この場合は、[Websocket](info:websocket) や [Server Sent Events](info:server-sent-events) のような別の方法が推奨されています。 diff --git a/5-network/10-long-polling/long-polling.svg b/5-network/10-long-polling/long-polling.svg new file mode 100644 index 0000000000..045ef371f5 --- /dev/null +++ b/5-network/10-long-polling/long-polling.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="521" height="320" viewBox="0 0 521 320"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="network" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="long-polling.svg"><text id="Browser" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x="27" y="74">Browser</tspan></text><text id="Server" fill="#181717" font-family="OpenSans-Regular, Open Sans" font-size="16" font-weight="normal"><tspan x="31" y="226">Server</tspan></text><path id="Line" fill="#181717" fill-rule="nonzero" d="M450.81 75.82l.435.244 8 4.5.775.436-.775.436-8 4.5-.436.245-.49-.872.436-.245 6.336-3.564H66.5v-1h390.591l-6.336-3.564-.436-.245.49-.872zM450.81 200.32l.435.244 8 4.5.775.436-.775.436-8 4.5-.436.245-.49-.872.436-.245L457.09 206H65.5v-1h391.591l-6.336-3.564-.436-.245.49-.872z"/><text id="request" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(76 113.291 143.266)"><tspan x="83.891" y="146.266">request</tspan></text><text id="connection-hangs" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="126" y="227">connection</tspan> <tspan x="142.519" y="245">hangs</tspan></text><text id="connection-breaks-en" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="12" font-weight="normal"><tspan x="186.09" y="264">connection breaks</tspan> <tspan x="196.479" y="282">end of request</tspan></text><path id="Line-3" fill="#C06334" fill-rule="nonzero" d="M90.233 80.79l.238.972 27.169 110.901 5.828-1.427L120 206.5l-10.13-11.932 5.827-1.429L88.529 82.238l-.238-.971 1.942-.476z"/><text id="data" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(-76 206.38 143.266)"><tspan x="189.58" y="146.266">data</tspan></text><path id="Line-2" fill="#C06334" fill-rule="nonzero" d="M233.226 82l3.107 15.341-5.794-1.565-29.574 109.485-.26.965-1.931-.521.26-.966L228.61 95.255l-5.792-1.565L233.226 82z"/><path id="Line-2-Copy" fill="#C06334" fill-rule="nonzero" d="M377.226 82l3.107 15.341-5.794-1.565-29.574 109.485-.26.965-1.931-.521.26-.966L372.61 95.255l-5.792-1.565L377.226 82z"/><text id="request-copy" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(76 260.291 143.266)"><tspan x="230.891" y="146.266">request</tspan></text><text id="connection-hangs-copy" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="270" y="227">connection</tspan> <tspan x="286.519" y="245">hangs</tspan></text><path id="Line-3-Copy-3" fill="#C06334" fill-rule="nonzero" d="M237.233 80.79l.238.972 27.169 110.901 5.828-1.427L267 206.5l-10.13-11.932 5.827-1.429-27.168-110.901-.238-.971 1.942-.476z"/><text id="request-copy-2" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(76 404.291 143.266)"><tspan x="374.891" y="146.266">request</tspan></text><path id="Line-3-Copy-4" fill="#C06334" fill-rule="nonzero" d="M381.233 80.79l.238.972 27.169 110.901 5.828-1.427L411 206.5l-10.13-11.932 5.827-1.429-27.168-110.901-.238-.971 1.942-.476z"/><text id="data-copy" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold" transform="rotate(-76 350.38 143.266)"><tspan x="333.58" y="146.266">data</tspan></text><path id="Line" stroke="#7E7C7B" stroke-dasharray="3,3" stroke-linecap="square" d="M235 40.75v201"/><text id="connection-breaks-en-copy" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="12" font-weight="normal"><tspan x="330.09" y="264">connection breaks</tspan> <tspan x="340.479" y="282">end of request</tspan></text><path id="Line-Copy" stroke="#7E7C7B" stroke-dasharray="3,3" stroke-linecap="square" d="M379 40.75v201"/></g></g></svg> \ No newline at end of file diff --git a/5-network/10-long-polling/longpoll.view/browser.js b/5-network/10-long-polling/longpoll.view/browser.js new file mode 100644 index 0000000000..3a66aa5c6d --- /dev/null +++ b/5-network/10-long-polling/longpoll.view/browser.js @@ -0,0 +1,54 @@ +// Sending messages, a simple POST +function PublishForm(form, url) { + + function sendMessage(message) { + fetch(url, { + method: 'POST', + body: message + }); + } + + form.onsubmit = function() { + let message = form.message.value; + if (message) { + form.message.value = ''; + sendMessage(message); + } + return false; + }; +} + +// Receiving messages with long polling +function SubscribePane(elem, url) { + + function showMessage(message) { + let messageElem = document.createElement('div'); + messageElem.append(message); + elem.append(messageElem); + } + + async function subscribe() { + let response = await fetch(url); + + if (response.status == 502) { + // Connection timeout + // happens when the connection was pending for too long + // let's reconnect + await subscribe(); + } else if (response.status != 200) { + // Show Error + showMessage(response.statusText); + // Reconnect in one second + await new Promise(resolve => setTimeout(resolve, 1000)); + await subscribe(); + } else { + // Got message + let message = await response.text(); + showMessage(message); + await subscribe(); + } + } + + subscribe(); + +} diff --git a/5-network/10-long-polling/longpoll.view/index.html b/5-network/10-long-polling/longpoll.view/index.html new file mode 100644 index 0000000000..7452c18386 --- /dev/null +++ b/5-network/10-long-polling/longpoll.view/index.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<script src="browser.js"></script> + +All visitors of this page will see messages of each other. + +<form name="publish"> + <input type="text" name="message" /> + <input type="submit" value="Send" /> +</form> + +<div id="subscribe"> +</div> + +<script> + new PublishForm(document.forms.publish, 'publish'); + // random url parameter to avoid any caching issues + new SubscribePane(document.getElementById('subscribe'), 'subscribe?random=' + Math.random()); +</script> diff --git a/5-network/10-long-polling/longpoll.view/server.js b/5-network/10-long-polling/longpoll.view/server.js new file mode 100644 index 0000000000..c3903e3750 --- /dev/null +++ b/5-network/10-long-polling/longpoll.view/server.js @@ -0,0 +1,87 @@ +let http = require('http'); +let url = require('url'); +let querystring = require('querystring'); +let static = require('node-static'); + +let fileServer = new static.Server('.'); + +let subscribers = Object.create(null); + +function onSubscribe(req, res) { + let id = Math.random(); + + res.setHeader('Content-Type', 'text/plain;charset=utf-8'); + res.setHeader("Cache-Control", "no-cache, must-revalidate"); + + subscribers[id] = res; + + req.on('close', function() { + delete subscribers[id]; + }); + +} + +function publish(message) { + + for (let id in subscribers) { + let res = subscribers[id]; + res.end(message); + } + + subscribers = Object.create(null); +} + +function accept(req, res) { + let urlParsed = url.parse(req.url, true); + + // new client wants messages + if (urlParsed.pathname == '/subscribe') { + onSubscribe(req, res); + return; + } + + // sending a message + if (urlParsed.pathname == '/publish' && req.method == 'POST') { + // accept POST + req.setEncoding('utf8'); + let message = ''; + req.on('data', function(chunk) { + message += chunk; + }).on('end', function() { + publish(message); // publish it to everyone + res.end("ok"); + }); + + return; + } + + // the rest is static + fileServer.serve(req, res); + +} + +function close() { + for (let id in subscribers) { + let res = subscribers[id]; + res.end(); + } +} + +// ----------------------------------- + +if (!module.parent) { + http.createServer(accept).listen(8080); + console.log('Server running on port 8080'); +} else { + exports.accept = accept; + + if (process.send) { + process.on('message', (msg) => { + if (msg === 'shutdown') { + close(); + } + }); + } + + process.on('SIGINT', close); +} diff --git a/5-network/11-websocket/article.md b/5-network/11-websocket/article.md new file mode 100644 index 0000000000..4c2b4411aa --- /dev/null +++ b/5-network/11-websocket/article.md @@ -0,0 +1,386 @@ +# WebSocket + +`WebSocket` プロトコルは仕様 [RFC 6455](http://tools.ietf.org/html/rfc6455) で説明されており、これは永続的な接続を介してブラウザとサーバ間でデータを交換する方法を提供します。接続の切断や追加のHTTPリクエストをすることなく、データを "パケット" として双方向に渡すことができます。 + +WebSocket は継続的にデータ交換を必要とするようなサービスに特に適しています。例えば、オンラインゲームやリアルタイムの取引システムなどです。 + +## 簡単な例 + +websocket の接続を開くには、url の特別なプロトコル `ws` を使用した `new WebSocket` を作る必要があります: + +```js +let socket = new WebSocket("*!*ws*/!*://javascript.info"); +``` + +暗号化された `wss://` プロトコルもあります。websocket の HTTPS 版のようなものです。 + +```smart header="常に `wss://` が好ましいです" +`wss://` プロトコルは暗号化されるだけでなく、より信頼性があります。 + +これは、`ws://` のデータは暗号化されておらず、あらゆる仲介者に見えるためです。古いプロキシサーバによっては WebSocket を認識しないため、おかしなヘッダに見え、接続を中止する可能性があります。 + +一方、`wss://` は WebSocket over TLS (HTTPS が HTTP over TLS であるのと同じ)であり、TLS は送信側でデータを暗号化し、受信側で復号化します。そのため、データパケットは暗号化されてプロキシを通過します。プロキシは中身を見ることはできず、それらを通過させます。 +``` + +ソケットが作成されると、そこで発生するイベントをリッスンする必要があります。全部で4つのイベントがあります。: +- **`open`** -- 接続が確立されました, +- **`message`** -- データを受け取りました, +- **`error`** -- websocket エラー, +- **`close`** -- 接続がクローズされました. + +...また、なにかを送信したいときは、`socket.send(data)` で送ることができます。 + +例: + +```js run +let socket = new WebSocket("wss://javascript.info/article/websocket/demo/hello"); + +socket.onopen = function(e) { + alert("[open] Connection established"); + alert("Sending to server"); + socket.send("My name is John"); +}; + +socket.onmessage = function(event) { + alert(`[message] Data received from server: ${event.data}`); +}; + +socket.onclose = function(event) { + if (event.wasClean) { + alert(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`); + } else { + // e.g. サーバのプロセスが停止、あるいはネットワークダウン + // この場合、event.code は通常 1006 になります + alert('[close] Connection died'); + } +}; + +socket.onerror = function(error) { + alert(`[error] ${error.message}`); +}; +``` + +デモ用に、上記の例では Node.js で書かれた小さなサーバ [server.js](demo/server.js)が動作しています。これは "Hello from server, John" と応答し、5秒待ってから接続をクローズします。 + +なので、 `open` -> `message` -> `close` のイベントが表示されるはずです。 + +これだけです。とても簡単ですね。 + +では、ここからはより詳しく見ていきましょう。 + +## websocket のオープン + +`new WebSocket(url)` が生成されると、すぐに接続が開始されます。 + +接続中、ブラウザは(ヘッダを利用して)サーバに問い合わせます: "Websocket をサポートしていますか?"。そしてサーバが "はい" と回答した場合、WebSocket プロトコルでやりとりが続きます。これは HTTP ではありません。 + +![](websocket-handshake.svg) + +これは `new WebSocket("wss://javascript.info/chat")` のリクエストにより作成されたブラウザヘッダの例です: + +``` +GET /chat +Host: javascript.info +Origin: https://javascript.info +Connection: Upgrade +Upgrade: websocket +Sec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q== +Sec-WebSocket-Version: 13 +``` + +- `Origin` -- クライアントページのオリジンです。例. `https://javascript.info`。WebSocket オブジェクトはもともとクロスオリジンです。特別なヘッダやその他の制限はありません。古いサーバはどのみち WebSocket を処理することができないので、互換性の問題はありません。ですが、`Origin` ヘッダは重要です。これによりサーバが Webサイトと WebSocket をやり取りするかを決めるからです。 +- `Connection: Upgrade` -- クライアントがプロトコルの変更を希望する合図です。 +- `Upgrade: websocket` -- リクエストされたプロトコルは "websocket" です。 +- `Sec-WebSocket-Key` -- セキュリティのための、ブラウザが生成したランダムなキーです。 +- `Sec-WebSocket-Version` -- WebSock プロトコルのバージョンです。現在は 13 です。 + +```smart header="WebSocket ハンドシェイクはエミュレートできません" +JavaScript はこれらのヘッダを設定することを許可していません。そのため、この手の HTTP リクエストを行うために、`XMLHttpRequest` や `fetch` を使うことはできません。 +``` + +サーバが WebSocket に切り替えることに同意すると、コード 101 の応答を返す必要があります: + +``` +101 Switching Protocols +Upgrade: websocket +Connection: Upgrade +Sec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g= +``` + +ここで、`Sec-WebSocket-Accept` は特別なアルゴリズムを使用して算出された `Sec-WebSocket-Key` です。ブラウザはこれを使って、リクエストに対応するレスポンスであることを確認します。 + +その後、データは WebSocket プロトコルを使用して転送されます。これからその構造を見てきましょう。なお、これは HTTP ではありません。 + +### 拡張とサブプロトコル + +拡張やサブプロトコルを記述する、追加のヘッダ `Sec-WebSocket-Extensions` と `Sec-WebSocket-Protocol` があります。 + +例: + +- `Sec-WebSocket-Extensions: deflate-frame` はブラウザがデータ圧縮をサポートすることを意味します。拡張はデータ転送に関するものあり、WebSocketプロトコルを拡張する機能です。ヘッダ `Sec-WebSocket-Extensions` は、サポートするすべての拡張のリストとともにブラウザによって自動的に送信されます。 +- `Sec-WebSocket-Protocol: soap, wamp` は任意のデータだけでなく、[SOAP](http://en.wikipedia.org/wiki/SOAP) あるいは WAMP ("The WebSocket Application Messaging Protocol") プロトコルのデータも転送することを意味します。WebSocket サブプロトコルは [IANA catalogue](http://www.iana.org/assignments/websocket/websocket.xml) に登録されています。 + + オプションのヘッダは、`new WebSocket` の第2引数(任意)で指定されたものであり、われわれのコードがどのサブプロトコルをサポートするかをサーバに伝えます。これはサブプロトコルの配列であり、例えば、SOAP あるいは WAMP を使いたい場合には次のようにします: + + ```js + let socket = new WebSocket("wss://javascript.info/chat", ["soap", "wamp"]); + ``` + +サーバは、使用に同意したプロトコルと拡張のリストを返す必要があります。 + +例えば、次のリクエストを見てください。: + +``` +GET /chat +Host: javascript.info +Upgrade: websocket +Connection: Upgrade +Origin: https://javascript.info +Sec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q== +Sec-WebSocket-Version: 13 +*!* +Sec-WebSocket-Extensions: deflate-frame +Sec-WebSocket-Protocol: soap, wamp +*/!* +``` + +応答例です: + +``` +101 Switching Protocols +Upgrade: websocket +Connection: Upgrade +Sec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g= +*!* +Sec-WebSocket-Extensions: deflate-frame +Sec-WebSocket-Protocol: soap +*/!* +``` + +ここでは、サーバは拡張 "deflate-frame" と、リクエストされたサブプロトコルのうち SOAP のみをサポートすると応答しています。 + +## データ転送 + +WebSocket 通信は "フレーム" (データフラグメント、どちら側からでも送信でき、いくつかの種類があります)で構成されます。 : + +- "text frames" -- 関係者が互いに送信するテキストデータを含んでいます。 +- "binary data frames" -- 関係者が互いに送信するバイナリデータを含んでいます。 +- "ping/pong frames" は接続確認に使用されます。サーバから送信され、ブラウザは自動でそれらに応答します。 +- "connection close frame" やその他いくつかのサービスフレームもあります。 + +ブラウザでは、テキストフレームまたはバイナリフレームのみを直接扱います。 + +**WebSocket `.send()` メソッドはテキストまたはバイナリデータを送信できます。** + +`socket.send(body)` 呼び出しは、文字列または `Blob` や `ArrayBuffer` などを含むバイナリ形式の `body` が許可されます。設定は必要ありません。任意のフォーマットで送信するだけでOKです。 + +**データを受信したとき、テキストは常に文字列として来ます。また、バイナリデータの場合は `Blob`、 `ArrayBuffer` 形式のいずれかを選択することができます。** + +これは `socket.bufferType` プロパティで設定されます。デフォルトは `"blob"` なので、バイナリデータは `Blob` オブジェクトで来ます。 + +[Blob](info:blob) は高レベルのバイナリオブジェクトで、`<a>`, `<img>` 等といったタグと直接統合されます。そのため、これは妥当なデフォルト値です。ただし、バリナリ処理の場合に個々のバイトデータにアクセスする必要があれば、`"arraybuffer"` に変更することができます。 + +```js +socket.bufferType = "arraybuffer"; +socket.onmessage = (event) => { + // event.data は文字列(テキストの場合)か arraybuffer (バイナリの場合)) です +}; +``` + +## レートリミット(Rate limiting) + +私たちのアプリが送信すべき大量のデータを生成していると想像してください。ですが、ユーザは低速のネットワーク接続で、恐らく郊外のモバイルインターネットだとします。 + +何度も `socket.send(data)` を呼び出すことはできますが、データはメモリにバッファ(保持)され、ネットワーク速度が許可する範囲でできるだけ早く送信されます。 + +`socket.bufferedAmount` プロパティはその時点でバッファされている(ネットワーク経由で送信されるのを待っている)バイト数が格納されています。 + +これを調べることで、ソケットが実際に送信可能かを確認することができます。 + +```js +// 100 ms 毎にソケットを検査し既存のデータがすべて送信されていれば追加のデータを送信します。 +setInterval(() => { + if (socket.bufferedAmount == 0) { + socket.send(moreData()); + } +}, 100); +``` + + +## 接続を閉じる + +通常、接続を閉じたいとき(ブラウザとサーバは同等の権限を持ちます)は、数値コードとテキストによる理由とを合わせて "connection close frame" を送信します。 + +そのためのメソッドは次の通りです: +```js +socket.close([code], [reason]); +``` + +- `code` は特別な WebSocket 終了コードです(オプション) +- `reason` は終了の理由を説明する文字列です(オプション) + +次に、`close` イベントハンドラの相手はそのコードと理由を取得します, e.g.: + +```js +// クローズする側: +socket.close(1000, "Work complete"); + +// 相手 +socket.onclose = event => { + // event.code === 1000 + // event.reason === "Work complete" + // event.wasClean === true (clean close) +}; +``` + +最も一般的なコード値です: + +- `1000` -- デフォルトで、通常のクローズです(`code` がない場合に使われます), +- `1006` -- 手動でこのコードにする方法はなく、接続が失われたことを示します(クローズフレームなし). + +次のようなコードもあります: + +- `1001` -- サーバがシャットダウンしたりブラウザがページを離れた場合など、当事者がどこかに去った, +- `1009` -- メッセージが大きすぎて処理できない, +- `1011` -- サーバでの予期しないエラー, +- ...など. + +全リストは [RFC6455, §7.4.1](https://tools.ietf.org/html/rfc6455#section-7.4.1) にあります。 + +WebSocket のコードは HTTP のコードにある程度似ていますが別物です。特に `1000` より小さい数字は予約されており、そのようなコードを設定しようとするとエラーになります。 + +```js +// 接続が壊れた場合 +socket.onclose = event => { + // event.code === 1006 + // event.reason === "" + // event.wasClean === false (no closing frame) +}; +``` + + +## 接続状態(Connection state) + +接続状態は次のような値をもつ `socket.readyState` プロパティで取得できます。: + +- **`0`** -- "CONNECTING": 接続はまだ確立されていません, +- **`1`** -- "OPEN": 接続は確立し通信中, +- **`2`** -- "CLOSING": 接続はクローズ中です, +- **`3`** -- "CLOSED": 接続はクローズされています. + + +## チャットのサンプル + +ブラウザの WebSocket API と Node.js WebSocket モジュール <https://github.com/websockets/ws> を使用してチャットのサンプルを見てみましょう。主にクライアントサイドに注目しますが、サーバも簡単です。 + +HTML: メッセージを送信するための `<form>` と受信メッセージ用の `<div>` が必要です: + +```html +<!-- message form --> +<form name="publish"> + <input type="text" name="message"> + <input type="submit" value="Send"> +</form> + +<!-- div with messages --> +<div id="messages"></div> +``` + +JavaScript から次の3つのことをします: +1. 接続をオープンします +2. フォームの送信 -- メッセージに対して `socket.send(message)` をします +3. メッセージの受信 -- `div#messages` に追加していきます + +これはそのコードです: + +```js +let socket = new WebSocket("wss://javascript.info/article/websocket/chat/ws"); + +// フォームからメッセージの送信をします +document.forms.publish.onsubmit = function() { + let outgoingMessage = this.message.value; + + socket.send(outgoingMessage); + return false; +}; + +// メッセージを受信しました - div#message にメッセージに表示します +socket.onmessage = function(event) { + let message = event.data; + + let messageElem = document.createElement('div'); + messageElem.textContent = message; + document.getElementById('messages').prepend(messageElem); +} +``` + +サーバサイドのコードは、少し今回のスコープを超えています。ここでは Node.js を使っていますが、そうでなくてもOKです。他のプラットフォームにも WebSocket でやり取りする手段があります。 + +サーバサイドのアルゴリズムは次の通りです: + +1. socket の集合 `clients = new Set()` を作成します。 +2. 受け入れられた各 websocket を `clients.add(socket)` で clients に追加し、`message` イベントリスナーを設定し、メッセージを取得します。 +3. メッセージを受信すると、clients をイテレートし全員に送信します。 +4. 接続を閉じられると、`clients.delete(socket)` をします。 + +```js +const ws = new require('ws'); +const wss = new ws.Server({noServer: true}); + +const clients = new Set(); + +http.createServer((req, res) => { + // ここでは websocket 接続のみを処理します + // 実際のプロジェクトでは、非-websocket リクエストを処理するコードがここにあります + wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onSocketConnect); +}); + +function onSocketConnect(ws) { + clients.add(ws); + + ws.on('message', function(message) { + message = message.slice(0, 50); // メッセージの最大長は 50 になります + + for(let client of clients) { + client.send(message); + } + }); + + ws.on('close', function() { + clients.delete(ws); + }); +} +``` + + +動作するサンプルです: + +[iframe src="chat" height="100" zip] + +ダウンロード(iframeの右上のボタン)をして、ローカルで実行することもできます。実行前に [Node.js](https://nodejs.org/en/) のインストールと `npm install ws` をするのを忘れないでください。 + +## サマリ + +WebSocket はブラウザ - サーバ間での永続的な接続を維持するだめのモダンな方法です。 + +- WebSocket にはクロスオリジン制約はありません +- ブラウザで十分サポートされています +- 文字列とバリナリデータを送受信するることができます + +API はシンプルです。 + +メソッド: +- `socket.send(data)`, +- `socket.close([code], [reason])`. + +イベント: +- `open`, +- `message`, +- `error`, +- `close`. + +WebSocket 自体には再接続や認証、その他の高レベルのメカニズムは含まれていません。そのため、それを実現するためのクライアント/サーバ ライブラリがあります。またこれらの機能を手動で実装することもできます。 + +WebSocket を既存のプロジェクトに統合するために、WebSocket サーバをメインの HTTP サーバを並行して実行し、単一のデータベースを共有する場合があります。WebSocket へのリクエストは WebSocket サーバにつながるサブドメイン `wss://ws.site.com` を使用し、`https://site.com` はメインの HTTP サーバはに行きます。 + +もちろん、他の統合方法も可能です。 diff --git a/5-network/11-websocket/chat.view/index.html b/5-network/11-websocket/chat.view/index.html new file mode 100644 index 0000000000..3dd4c24cd9 --- /dev/null +++ b/5-network/11-websocket/chat.view/index.html @@ -0,0 +1,39 @@ +<!doctype html> +<form name="publish"> + <input type="text" name="message" maxlength="50"/> + <input type="submit" value="Send"/> +</form> + +<div id="messages"></div> + +<script> +let url = location.host == 'localhost' ? + 'ws://localhost:8080/ws' : location.host == 'javascript.local' ? + `ws://javascript.local/article/websocket/chat/ws` : // dev integration with local site + `wss://javascript.info/article/websocket/chat/ws`; // prod integration with javascript.info + +let socket = new WebSocket(url); + +// send message from the form +document.forms.publish.onsubmit = function() { + let outgoingMessage = this.message.value; + + socket.send(outgoingMessage); + return false; +}; + +// handle incoming messages +socket.onmessage = function(event) { + let incomingMessage = event.data; + showMessage(incomingMessage); +}; + +socket.onclose = event => console.log(`Closed ${event.code}`); + +// show message in div#messages +function showMessage(message) { + let messageElem = document.createElement('div'); + messageElem.textContent = message; + document.getElementById('messages').prepend(messageElem); +} +</script> diff --git a/5-network/11-websocket/chat.view/server.js b/5-network/11-websocket/chat.view/server.js new file mode 100644 index 0000000000..d21973fc75 --- /dev/null +++ b/5-network/11-websocket/chat.view/server.js @@ -0,0 +1,61 @@ +/** +Before running: +> npm install ws +Then: +> node server.js +> open http://localhost:8080 in the browser +*/ + +const http = require('http'); +const fs = require('fs'); +const ws = new require('ws'); + +const wss = new ws.Server({noServer: true}); + +const clients = new Set(); + +function accept(req, res) { + + if (req.url == '/ws' && req.headers.upgrade && + req.headers.upgrade.toLowerCase() == 'websocket' && + // can be Connection: keep-alive, Upgrade + req.headers.connection.match(/\bupgrade\b/i)) { + wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onSocketConnect); + } else if (req.url == '/') { // index.html + fs.createReadStream('./index.html').pipe(res); + } else { // page not found + res.writeHead(404); + res.end(); + } +} + +function onSocketConnect(ws) { + clients.add(ws); + log(`new connection`); + + ws.on('message', function(message) { + log(`message received: ${message}`); + + message = message.slice(0, 50); // max message length will be 50 + + for(let client of clients) { + client.send(message); + } + }); + + ws.on('close', function() { + log(`connection closed`); + clients.delete(ws); + }); +} + +let log; +if (!module.parent) { + log = console.log; + http.createServer(accept).listen(8080); +} else { + // to embed into javascript.info + log = function() {}; + // log = console.log; + exports.accept = accept; +} diff --git a/5-network/11-websocket/demo.view/server.js b/5-network/11-websocket/demo.view/server.js new file mode 100644 index 0000000000..111a7ce757 --- /dev/null +++ b/5-network/11-websocket/demo.view/server.js @@ -0,0 +1,35 @@ +const http = require('http'); +const ws = require('ws'); + +const wss = new ws.Server({noServer: true}); + +function accept(req, res) { + // all incoming requests must be websockets + if (!req.headers.upgrade || req.headers.upgrade.toLowerCase() != 'websocket') { + res.end(); + return; + } + + // can be Connection: keep-alive, Upgrade + if (!req.headers.connection.match(/\bupgrade\b/i)) { + res.end(); + return; + } + + wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onConnect); +} + +function onConnect(ws) { + ws.on('message', function (message) { + let name = message.match(/([\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]+)$/gu) || "Guest"; + ws.send(`Hello from server, ${name}!`); + + setTimeout(() => ws.close(1000, "Bye!"), 5000); + }); +} + +if (!module.parent) { + http.createServer(accept).listen(8080); +} else { + exports.accept = accept; +} diff --git a/5-network/11-websocket/websocket-handshake.svg b/5-network/11-websocket/websocket-handshake.svg new file mode 100644 index 0000000000..a8ec2389ab --- /dev/null +++ b/5-network/11-websocket/websocket-handshake.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="429" height="348" viewBox="0 0 429 348"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="network" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="websocket-handshake.svg"><path id="Rectangle-227" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M2 16h128v64H2z"/><text id="Browser" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="40" y="49">Browser</tspan></text><path id="Rectangle-228" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M298 16h128v64H298z"/><text id="Server" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="340" y="49">Server</tspan></text><path id="Line" stroke="#7E7C7B" stroke-linecap="square" d="M67 81v250.5M363 81v251.5"/><path id="Line" fill="#C06334" fill-rule="nonzero" d="M349 133l14 7-14 7v-6H68v-2h281v-6z"/><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M83 204v6h281v2H83v6l-14-7 14-7z"/><text id="HTTP-request" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="172.015" y="130">HTTP-request</tspan></text><text id=""Hey,-server,-let's" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="102.605" y="161">"Hey, server, let's talk WebSocket?"</tspan></text><text id="HTTP-response-"Okay!" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="166.419" y="204">HTTP-response</tspan> <tspan x="191.972" y="226">"Okay!"</tspan></text><path id="Line-Copy-2" fill="#C06334" fill-rule="nonzero" d="M81 272v6h2v2h-2v6l-14-7 14-7zm268 0l14 7-14 7v-14zm-260 6v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4zm6 0v2h-4v-2h4z"/><text id="WebSocket-protocol" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="151.604" y="272">WebSocket protocol</tspan></text></g></g></svg> \ No newline at end of file diff --git a/5-network/12-server-sent-events/article.md b/5-network/12-server-sent-events/article.md new file mode 100644 index 0000000000..939a17f99d --- /dev/null +++ b/5-network/12-server-sent-events/article.md @@ -0,0 +1,270 @@ +# Server Sent Events + +[Server-Sent Events](https://html.spec.whatwg.org/multipage/comms.html#the-eventsource-interface) の仕様では、サーバとの接続を維持し、サーバからイベントを受けとれるようにする組み込みのクラス `EventSource` について説明されています。 + +`WebSocket` と同様に接続は永続的ですが、重要な違いがいくつかあります: + +| `WebSocket` | `EventSource` | +|-------------|---------------| +| 双方向: クライアントとサーバ両方がメッセージのやり取りをすることができます | 単方向: サーバのみがデータを送信します | +| バリナリデータとテキストデータ | テキストのみ | +| WebSocket プロトコル | 通常の HTTP | + +`EventSource` は `WebSocket` ほど強力ではない、サーバと通信する方法です。 + +なぜこれを使う必要があるのでしょう? + +主たる理由はより簡単だからです。多くのアプリケーションでは、`WebSocket` は少し強力すぎます。 + +サーバからデータストリームを受信する必要のある、例えばチャットメッセージやマーケットプレイスなどが `EventSource` の得意なところです。また、`WebSocket` では手動で何らかの実装が必要な自動再接続もサポートしています。その上、新しいプロトコルではなく通常の HTTP です。 + +## メッセージの取得 + +メッセージの受信を開始するには、`new EventSource(url)` を作成するだけです。 + +ブラウザは `url` に接続し、接続を開いたままにしてイベントを待ちます。 + +サーバはステータス 200 とヘッダ `Content-Type: text/event-stream` で応答し、接続を維持しつつ次のような特別な形式でメッセージを書き込みます。: + +``` +data: Message 1 + +data: Message 2 + +data: Message 3 +data: of two lines +``` + +- メッセージテキストは `data:` の後に続きます。コロンの後のスペースは任意です。 +- メッセージは2つの改行 `\n\n` で区切られます。 +- 改行 `\n` を送るには、すぐにもう一つ `data:` を指定します(上の3番目のメッセージ)。 + +実際には、複雑なメッセージは通常 JSON エンコードされて送信されます。改行は `\n` としてエンコードされるため、複数の `data:` メッセージは必要ありません。 + +例: + +```js +data: {"user":"John","message":"First line*!*\n*/!* Second line"} +``` + +...そのため、1つの `data:` が1つのメッセージを持つと想定できます。 + +このようなメッセージごとに、`message` イベントが生成されます。: + +```js +let eventSource = new EventSource("/events/subscribe"); + +eventSource.onmessage = function(event) { + console.log("New message", event.data); + // 上のデータストリームでは、3回ログが出力されます +}; + +// or eventSource.addEventListener('message', ...) +``` + +### クロスオリジンリクエスト + +`EventSource` は `fetch` など他のネットワークメソッドのように、クロスオリジンリクエストをサポートします。任意のURLを使用することができます。 + +```js +let source = new EventSource("https://another-site.com/events"); +``` + +リモートサーバは `Origin` ヘッダを取得し、処理を続けるには `Access-Control-Allow-Origin` を応答する必要があります。 + +クレデンシャルを渡す場合は、次のように `withCredentials` オプションで設定します。: + +```js +let source = new EventSource("https://another-site.com/events", { + withCredentials: true +}); +``` + +クロスオリジンヘッダに関しての詳細は、チャプター <info:fetch-crossorigin> を参照してください。 + + +## 再接続 + +作成時に `new EventSource` はサーバに接続し、接続が切れた場合は再接続します。 + +気にする必要がないのでとても便利です。 + +再接続の間にはわずかな遅延があり、デフォルトでは数秒程度です。 + +サーバは応答の中で `retry` を使用して推奨する遅延を設定することができます(ミリ秒単位)。 + +```js +retry: 15000 +data: Hello, I set the reconnection delay to 15 seconds +``` + +`retry:` は他のデータを一緒に来ることもあれば、単独のメッセージとしてくることもあります。 + +ブラウザは再接続する前に、指定されたミリ秒またはそれ以上待つ必要があります。e.g. ブラウザが現時点ではネットワーク接続がないことを知っている場合は、接続されるまで待ってからリトライします。 + +- サーバがブラウザが再接続するのを止めてほしい場合は、HTTP ステータス 204 で応答します。 +- ブラウザが接続を閉じたい場合は、`eventSource.close()` を呼び出します。 + +```js +let eventSource = new EventSource(...); + +eventSource.close(); +``` + +また、応答の `Content-Type` が正しくない、あるいは 301, 307, 200, 204 以外の HTTP ステータスである場合、再接続は行われません。接続では `"error"` イベントが発生し、ブラウザは再接続を行いません。 + +```smart +接続が最終的に閉じられると、それを "再オープン" することはできません。再び接続したい場合は新たに `EventSource` を作成します。 +``` + +## Message id + +ネットワークの問題により接続が切れた場合、どちらの側もどのメッセージを受信しており、どのメッセージを受信していないのかが確認できません。 + +そのため接続を正しく再開するには各メッセージに次のような `id` フィールドが必要です。: + +``` +data: Message 1 +id: 1 + +data: Message 2 +id: 2 + +data: Message 3 +data: of two lines +id: 3 +``` + +`id` を持つメッセージを受け取ると、ブラウザは次のことを行います: + +- プロパティ `eventSource.lastEventId` にその値を設定します。 +- 再接続でその `id` が設定されたヘッダ `Last-Event-ID` を送信し、サーバが次のメッセージを再送信できるようにします。 + + +```smart header="`data:` の後に `id:` を置きます" +注意: `id` はサーバによりメッセージ `data` の下に追加されます。これは、メッセージを受信した後に `lastEventId` が更新されるようにするためです。 +``` + +## 接続ステータス: readyState + +`EventSource` オブジェクトには `readyState` プロパティがあり、次のいずれかの値を取ります: + +```js no-beautify +EventSource.CONNECTING = 0; // 接続中 or 再接続中 +EventSource.OPEN = 1; // 接続済み +EventSource.CLOSED = 2; // 接続終わり +``` + +オブジェクトが作成されるか、接続がダウンしたときは常に `EventSource.CONNECTING` (= `0`) になります。 + +このプロパティを確認することで、`EventSource` の状態を知ることができます。 + +## イベントタイプ + +デフォルトでは `EventSource` オブジェクトは 3つのイベントを生成します: + +- `message` -- メッセージを受信。 メッセージ自体は `event.data` で利用できます。 +- `open` -- 接続が開いた. +- `error` -- 接続が確立できなかった e.g. サーバが HTTP ステータス 500 を返した。 + +サーバはイベント開始時に `event: ...` で別の種類のイベントを指定することもできます。 + +例: + +``` +event: join +data: Bob + +data: Hello + +event: leave +data: Bob +``` + +カスタムイベントを扱うには、`onmessage` ではなく `addEventListener` を利用する必要があります: + +```js +eventSource.addEventListener('join', event => { + alert(`Joined ${event.data}`); +}); + +eventSource.addEventListener('message', event => { + alert(`Said: ${event.data}`); +}); + +eventSource.addEventListener('leave', event => { + alert(`Left ${event.data}`); +}); +``` + +## 完全な例 + +これは、`1`, `2`, `3`, 次に `bye` を送信し、その後接続を切断するサーバです。 + +その後、ブラウザは自動的に再接続します。 + +[codetabs src="eventsource"] + +## サマリ + +`EventSource` オブジェクトは自動的に永続的な接続を確立し、サーバがメッセージを送信できるようにします。 + +次のものを提供します: +- 自動再接続。`retry` で再試行タイムアウトの調整が可能です。 +- イベントを再開するためのメッセージID。最後に受信した識別子は、再接続する際に `Last-Event-ID` ヘッダで送信されます。 +- 現在のステータスは `readyState` で見ることができます。 + +これにより、`EventSource` は `WebSocket` ( WebSocket はより低レベルで、このような組み込み機能はありません(もちろん、実装するこはできます))の代替手段になります。 + +多くの実際のアプリケーションでは、`EventSource` の機能で十分です。 + +すべてのモダンブラウザー(IEは除く)でサポートされています。 + +構文は次の通りです: + +```js +let source = new EventSource(url, [credentials]); +``` + +2番目の引数には、可能なオプションが1つだけあります: `{ withCredentials: true }`, これはクロスオリジン資格情報を送信することを許可することを意味します。 + +全体的なクロスオリジンセキュリティは `fetch` や他のネットワーク手段と同じです。 + +### `EventSource` オブジェクトのプロパティ + +`readyState` +: 現在の接続状態: `EventSource.CONNECTING (=0)`, `EventSource.OPEN (=1)` あるいは `EventSource.CLOSED (=2)`. + +`lastEventId` +: 最後の受信した `id`. 再接続すると、ブラウザはヘッダ `Last-Event-ID` でその値を送信します。 + +### メソッド + +`close()` +: 接続を閉じます + +### イベント + +`message` +: メッセージの受信, データは `event.data` にあります。 + +`open` +: 接続が確立されました。 + +`error` +: 接続の切断(自動再接続されます)や致命的なエラーの両方を含むエラーの場合です。再接続が試行されているかは `readyState` をチェックすることが確認できます。 + +サーバは `event:` でカスタムイベント名を設定することができます。このようなイベントは `on<event>` ではなく、`addEventListener` で処理する必要があります。 + +### サーバレスポンスフォーマット + +サーバは `\n\n` で区切られたメッセージを送信します。 + +メッセージには次のフィールドが含まれます: + +- `data:` -- メッセージ本文。複数の一連の `data` は各パート間に `\n` を含む単一のメッセージとして解釈されます。 +- `id:` -- 再接続時に `Last-Event-ID` で送信される `lastEventId` を更新します。 +- `retry:` -- 再接続のリトライ遅延をミリ秒で指定します。JavaScript からこの値を設定することはできません。 +- `event:` -- イベント名。`data:` の前になければなりません。 + +メッセージには任意の順序で1つ以上のフィールドを含めることができますが、`id:` は通常最後にあります。 diff --git a/5-network/12-server-sent-events/eventsource.view/index.html b/5-network/12-server-sent-events/eventsource.view/index.html new file mode 100644 index 0000000000..795b07ebb2 --- /dev/null +++ b/5-network/12-server-sent-events/eventsource.view/index.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<script> +let eventSource; + +function start() { // when "Start" button pressed + if (!window.EventSource) { + // IE or an old browser + alert("The browser doesn't support EventSource."); + return; + } + + eventSource = new EventSource('digits'); + + eventSource.onopen = function(e) { + log("Event: open"); + }; + + eventSource.onerror = function(e) { + log("Event: error"); + if (this.readyState == EventSource.CONNECTING) { + log(`Reconnecting (readyState=${this.readyState})...`); + } else { + log("Error has occured."); + } + }; + + eventSource.addEventListener('bye', function(e) { + log("Event: bye, data: " + e.data); + }); + + eventSource.onmessage = function(e) { + log("Event: message, data: " + e.data); + }; +} + +function stop() { // when "Stop" button pressed + eventSource.close(); + log("eventSource.close()"); +} + +function log(msg) { + logElem.innerHTML += msg + "<br>"; + document.documentElement.scrollTop = 99999999; +} +</script> + +<button onclick="start()">Start</button> Press the "Start" to begin. +<div id="logElem" style="margin: 6px 0"></div> + +<button onclick="stop()">Stop</button> "Stop" to finish. diff --git a/5-network/12-server-sent-events/eventsource.view/server.js b/5-network/12-server-sent-events/eventsource.view/server.js new file mode 100644 index 0000000000..34c7b1253e --- /dev/null +++ b/5-network/12-server-sent-events/eventsource.view/server.js @@ -0,0 +1,47 @@ +let http = require('http'); +let url = require('url'); +let querystring = require('querystring'); + +function onDigits(req, res) { + res.writeHead(200, { + 'Content-Type': 'text/event-stream; charset=utf-8', + 'Cache-Control': 'no-cache' + }); + + let i = 0; + + let timer = setInterval(write, 1000); + write(); + + function write() { + i++; + + if (i == 4) { + res.write('event: bye\ndata: bye-bye\n\n'); + clearInterval(timer); + res.end(); + return; + } + + res.write('data: ' + i + '\n\n'); + + } +} + +function accept(req, res) { + + if (req.url == '/digits') { + onDigits(req, res); + return; + } + + fileServer.serve(req, res); + +} + + +if (!module.parent) { + http.createServer(accept).listen(8080); +} else { + exports.accept = accept; +} diff --git a/5-network/index.md b/5-network/index.md new file mode 100644 index 0000000000..eea487e75d --- /dev/null +++ b/5-network/index.md @@ -0,0 +1,2 @@ + +# ネットワークリクエスト diff --git a/5-regular-expressions/01-regexp-introduction/article.md b/5-regular-expressions/01-regexp-introduction/article.md deleted file mode 100644 index 978f8801e5..0000000000 --- a/5-regular-expressions/01-regexp-introduction/article.md +++ /dev/null @@ -1,130 +0,0 @@ -# パターンとフラグ - -正規表現(Regular expressions)は文字列内を検索したり置換するための強力な方法です。 - -JavaScriptでは、正規表現は組み込みの `RegExp` クラスのオブジェクトを使用して実装され、文字列と統合されています。 - -正規表現はプログラミング言語によって異なることに留意してください。このチュートリアルでは、JavaScript に焦点を当てます。もちろん共通点は多いですが、Perl, Ruby, PHP などとは多少異なります。 - -[cut] - -## 正規表現 - -正規表現(もしくは "regexp", または単に "reg") は *パターン* とオプションの *フラグ* で構成されています。 - -正規表現オブジェクトを生成するための2つの構文があります。 - -長い構文: - -```js -regexp = new RegExp("pattern", "flags"); -``` - -...そして短い構文です。スラッシュ `"/"` を使います: - -```js -regexp = /pattern/; // フラグなし -regexp = /pattern/gmi; // g, m と i のフラグあり(詳細は後ほど説明します) -``` - -スラッシュ `"/"` は正規表現を作成していることを JavaScript に伝えます。文字列の引用符と同じ役割を果たします。 - -## 使用方法 - -文字列内を検索するためには、メソッド [search](mdn:js/String/search) を使うことができます。 - -例: - -```js run -let str = "I love JavaScript!"; // ここを検索します - -let regexp = /love/; -alert( str.search(regexp) ); // 2 -``` - -`str.search` メソッドはパターン `pattern:/love/` を探し、文字列内での位置を返します。ご推測の通り、 `pattern:/love/` は最もシンプルなパターンです。それは簡単な部分文字列検索です。 - -上のコードは次と同じです: - -```js run -let str = "I love JavaScript!"; // ここを検索します - -let substr = 'love'; -alert( str.search(substr) ); // 2 -``` - -したがって、`pattern:/love/` の検索は `"love"` の検索と同じです。 - -しかし、それは今だけです。すぐにより強力な検索機能を備えた、より複雑な正規表現作成していきます。 - -```smart header="色" -ここからの配色は次の通りです: - -- 正規表現 -- `pattern:red` -- 文字列 (検索する場所) -- `subject:blue` -- 結果 -- `match:green` -``` - - -````smart header="いつ `new RegExp` を使いますか?" -通常は短い構文である `/.../` を使います。しかし、これは変数の挿入を許可していないため、コードを書く時点で正確な正規表現を知っていなければなりません。 - -一方、`new RegExp` は文字列から動的にパターンを構築することができます。 - -したがって、検索するために必要なことを理解し、そこから `new RegExp` を作ることができます。: - -```js run -let search = prompt("What you want to search?", "love"); -let regexp = new RegExp(search); - -// ユーザが望むものを見つける -alert( "I love JavaScript".search(regexp)); -``` -```` - - -## フラグ - -正規表現には検索に影響を与えるフラグを含んでいる場合があります。 - -JavaScript には 5 つしかありません: - -`i` -: このフラグを指定すると、検索は大文字小文字を区別しません: `A` と `a` に違いはありません(下の例をみてください)。 - -`g` -: このフラグを指定すると、検索はすべての一致を探します。指定がない場合は -- 最初の1つのみを探します(次のチャプターで使い方を見ていきます)。 - -`m` -: 複数行モードです(チャプター <info:regexp-multiline> で説明します)。 - -`u` -: 完全なユニコードサポートを有効にします。このフラグはサロゲートペアの正しい処理を可能にします。より詳細についてはチャプター <info:regexp-unicode> を参照してください。 - -`y` -: スティッキーモード([次のチャプター](info:regexp-methods#y-flag) で説明します)。 - -## "i" フラグ - -最も簡単なフラグは `i` です。 - -その例です: - -```js run -let str = "I love JavaScript!"; - -alert( str.search(/LOVE/) ); // -1 (見つからない) -alert( str.search(/LOVE/i) ); // 2 -``` - -1. 最初の検索は `-1` (見つからない) を返します。なぜなら、デフォルトでは検索は大文字小文字を区別するためです。 -2. フラグ `pattern:/LOVE/i` を指定すると、検索は位置 2 に `match:love` を見つけます。 - -したがって、`i` フラグはすでに単純な部分文字列検索よりも強力な正規表現を作成します。しかし、まだまだはるかに多くのことがあります。次のチャプターでは、他のフラグと機能についても説明します。 - - -## サマリ - -- 正規表現はパターンとオプションのフラグ `g`, `i`, `m`, `u`, `y` で構成されます。 -- フラグと後で学ぶ特別な記号がなければ、正規表現による検索は部分文字列検索と同じです。 -- メソッド `str.search(regexp)` は一致するものが見つかった場所はそのインデックスを返します。見つからなかった場合は `-1` を返します。 diff --git a/5-regular-expressions/03-regexp-character-classes/1-find-time-hh-mm/solution.md b/5-regular-expressions/03-regexp-character-classes/1-find-time-hh-mm/solution.md deleted file mode 100644 index a365dba28f..0000000000 --- a/5-regular-expressions/03-regexp-character-classes/1-find-time-hh-mm/solution.md +++ /dev/null @@ -1,6 +0,0 @@ - -解答: `pattern:\b\d\d:\d\d\b`. - -```js run -alert( "Breakfast at 09:00 in the room 123:456.".match( /\b\d\d:\d\d\b/ ) ); // 09:00 -``` diff --git a/5-regular-expressions/03-regexp-character-classes/1-find-time-hh-mm/task.md b/5-regular-expressions/03-regexp-character-classes/1-find-time-hh-mm/task.md deleted file mode 100644 index abef647786..0000000000 --- a/5-regular-expressions/03-regexp-character-classes/1-find-time-hh-mm/task.md +++ /dev/null @@ -1,8 +0,0 @@ -# 時間を見つけて - -時間にはフォーマット `hours:minutes` があります。`09:00` のように時と分は両方とも2桁の数字です。 - -文字列 `subject:Breakfast at 09:00 in the room 123:456.` で時間を見つける正規表現を作成してください。 - -P.S. このタスクではまだ時間の正しさをチェックをする必要はありません。なので、`25:99` も有効な結果です。 -P.P.S 正規表現は `123:456` にはマッチしないでください。 diff --git a/5-regular-expressions/03-regexp-character-classes/article.md b/5-regular-expressions/03-regexp-character-classes/article.md deleted file mode 100644 index 03c73507d2..0000000000 --- a/5-regular-expressions/03-regexp-character-classes/article.md +++ /dev/null @@ -1,228 +0,0 @@ -# 文字クラス - -実践的なタスクを考えてみましょう -- `"+7(903)-123-45-67"` という電話番号があり、その文字列のすべての数字を見つける必要があります。他の文字に興味はありません。 - -文字クラスは集合から任意の記号にマッチする特別な表記法です。 - -[cut] - -例えば、"数字" クラスがあります。それは `\d` と書きます。それを正規表現のパターンに入れ検索すると、任意の数字がそれにマッチします。 - -例えば、正規表現 `pattern:/\d/` は1つの数字を探します: - -```js run -let str = "+7(903)-123-45-67"; - -let reg = /\d/; - -alert( str.match(reg) ); // 7 -``` - -上の例では、正規表現はグローバルではありません。なので、最初のマッチだけを探します。 - -すべての数字を探すために `g` フラグを追加しましょう: - -```js run -let str = "+7(903)-123-45-67"; - -let reg = /\d/g; - -alert( str.match(reg) ); // マッチした配列: 7,9,0,3,1,2,3,4,5,6,7 -``` - -## 最もよく使われるクラス: \d \s \w - -先程は数字のための文字クラスでした。同様に他の文字クラスがあります。 - -最も使われるのは: - -`\d` ("d" は "digit" より) -: 数字: `0` から `9` の文字です。 - -`\s` ("s" は "space" より) -: スペース記号: スペース、タブ、改行が含まれます。 - -`\w` ("w" は "word" より) -: "言葉" の文字: 英語アルファベットの文字または数字またはアンダースコア。 非英語の文字(キリル文字やヒンディー語など)は `\w` に属しません。 - -例えば、`pattern:\d\s\w` は `"1 Z"` のように、数字のあとに空白文字、単語文字が続く文字列を意味します。 - -正規表現は通常の記号と文字クラス両方を含む場合があります。 - -例として、`pattern:CSS\d` は `match:CSS` のあとに数字が続く文字列にマッチします。: - -```js run -let str = "CSS4 is cool"; -let reg = /CSS\d/ - -alert( str.match(reg) ); // CSS4 -``` - -また、多くの文字クラスを使うこともできます: - -```js run -alert( "I love HTML5!".match(/\s\w\w\w\w\d/) ); // 'HTML5' -``` - -マッチ(各文字クラスは結果文字に対応します): - -![](love-html5-classes.png) - -## 単語境界: \b - -単語境界 `pattern:\b` -- は特別な文字クラスです。 - -これは文字を表すのではなく、文字の境界を表します。 - -例えば、 `pattern:\bJava\b` は `subject:Hello, Java!` という文字列の `match:Java` とマッチしますが、`subject:Hello, JavaScript!` という文字列にはマッチしません。: - -```js run -alert( "Hello, Java!".match(/\bJava\b/) ); // Java -alert( "Hello, JavaScript!".match(/\bJava\b/) ); // null -``` - -通常、文字クラスは結果の文字(単語や数字のような)を意味するので、ある意味では、境界は "ゼロ幅" を持ちますが、この場合はそうではありません。 - -境界はテストです。 - -正規表現エンジンが検索を行っているとき、マッチを見つけるために文字列に沿って移動しています。各文字列の位置で、パターンを検索しようとします。 - -パターンに `pattern:\b` が含まれている場合、文字列の位置が次の条件のいずれかを満たしているか検査します: - -- 文字列が開始し、最初の文字が `\w` である。 -- 文字列が終わり、最後の文字が `\w` である。 -- 文字列の内側: 片方が `\w` で、もう一方が `\w` でない。 - -例えば、`subject:Hello, Java!` の文字列で `\b` にマッチする位置は次の通りです: - -![](hello-java-boundaries.png) - -したがって、`pattern:\bHello\b` と `pattern:\bJava\b` はマッチしますが、`pattern:\bHell\b` (`l` のあとに単語境界がないため) と `Java!\b` (感嘆符は `\w` でマッチする文字ではないので、単語境界がありません)はマッチしません。 - -```js run -alert( "Hello, Java!".match(/\bHello\b/) ); // Hello -alert( "Hello, Java!".match(/\bJava\b/) ); // Java -alert( "Hello, Java!".match(/\bHell\b/) ); // null -alert( "Hello, Java!".match(/\bJava!\b/) ); // null -``` - -`pattern:\b` は検索エンジンに境界をテストさせます。それにより、`pattern:Java\b` は単語境界が続く場合にのみ `match:Java` を検出しますが、結果には文字を追加しないことに改めて留意しましょう。 - -通常、スタンドアロンの英単語を見つけるのに `\b` を使います。そのため、`"Java"` が必要な場合、`pattern:\bJava\b` は正確にスタンドアロンの単語を見つけ、`"JavaScript"` の一部であるときは無視します。 - -別の例: 正規表現 `pattern:\b\d\d\b` はスタンドアロンの 2桁の数字を探します。つまり、前後の `pattern:\d\d` は `\w` (もしくは文字列の始め/終わり)とは異なる記号でなければなりません。 - -```js run -alert( "1 23 456 78".match(/\b\d\d\b/g) ); // 23,78 -``` - -```warn header="単語境界は英語以外のアルファベットでは機能しません" -単語境界チェック `\b` は `\w` と他のものとの間の境界をテストします。しかし、 `\w` は英語文字(もしくは数字かアンダースコア)を意味するので、他の文字(キリル文字や象形文字など)の場合は機能しません。 -``` - - -## 逆のクラス - -すべての文字クラスには "逆のクラス" があります。それは同じ文字ですが大文字で表されます。 - -"逆" は、他のすべての文字にマッチすることを意味します。例えば: - -`\D` -: 非数字: `\d` 以外の任意の字です。例えば文字です。 - -`\S` -: 非スペース: `\s` 以外のすべての文字です。例えば文字です。 - -`\W` -: 非単語文字: `\w` 以外の文字。 - -`\B` -: 非境界: `\b` と逆のテストです。 - -チャプターの先頭では、電話番号 `subject:+7(903)-123-45-67` からすべての数字を取得する方法を見ました。文字列から "純粋な" 電話番号を取得してみましょう。: - -```js run -let str = "+7(903)-123-45-67"; - -alert( str.match(/\d/g).join('') ); // 79031234567 -``` - -代わりの方法は、文字列から非数字を見つけ削除することです: - - -```js run -let str = "+7(903)-123-45-67"; - -alert( str.replace(/\D/g, "") ); // 79031234567 -``` - -## 空白は通常の文字です - -正規表現は空白を含む場合があることに注意してください。空白は普通の文字のように扱われます。 - -通常、私たちは空白にはあまり注意を払っていません。我々にとって文字列 `subject:1-5` と `subject:1 - 5` はほぼ同じです。 - -しかし、正規表現は空白を考慮しなければ期待通りに動作しません。 - -ダッシュで分離されている数字を見つけましょう。: - -```js run -alert( "1 - 5".match(/\d-\d/) ); // null, マッチしません! -``` - -これは正規表現の中に空白を追加して修正したものです: - -```js run -alert( "1 - 5".match(/\d - \d/) ); // 1 - 5, これは動作します -``` - -もちろん、空白はそれらを探す場合にだけ必要です。余分な空白は(単に別の他のとゆうな文字と同じように)マッチするのを妨げます。: - -```js run -alert( "1-5".match(/\d - \d/) ); // null, 文字列 1-5 には空白がないからです -``` - -つまり、正規表現ではすべての文字が重要です。空白もです。 - -## ドットは任意の文字です - -ドット `"."` は *改行を除く任意の文字* にマッチする特別な文字クラスです。 - -例: - -```js run -alert( "Z".match(/./) ); // Z -``` - -また正規表現の中にある場合: - -```js run -let reg = /CS.4/; - -alert( "CSS4".match(reg) ); // CSS4 -alert( "CS-4".match(reg) ); // CS-4 -alert( "CS 4".match(reg) ); // CS 4 (空白も文字です) -``` - -ドットは "任意の文字" を意味しますが、 "文字の欠如" ではないことに注意してください。それにマッチする文字が必要です:: - -```js run -alert( "CS4".match(/CS.4/) ); // null, ドットに対する文字がないのでマッチしません -``` - - -## サマリ - -文字クラスを説明しました: - -- `\d` -- 数字 -- `\D` -- 非数字 -- `\s` -- スペース記号、タブ、改行 -- `\S` -- `\s` 以外 -- `\w` -- 英語文字、数字、アンダースコア `'_'` -- `\W` -- `\w` 以外 -- `'.'` -- 改行以外の任意の文字 - -バックスラッシュやドットのような特別な意味を持つ文字を検索したい場合、バックスラッシュ `pattern:\.` でエスケープする必要があります。 - -正規表現は改行 `\n` と言った特別な文字も含むことに注意してください。 他の文字が使用されているので、文字クラスとの競合はありません。 diff --git a/5-regular-expressions/03-regexp-character-classes/hello-java-boundaries.png b/5-regular-expressions/03-regexp-character-classes/hello-java-boundaries.png deleted file mode 100644 index ba293e6dae..0000000000 Binary files a/5-regular-expressions/03-regexp-character-classes/hello-java-boundaries.png and /dev/null differ diff --git a/5-regular-expressions/03-regexp-character-classes/hello-java-boundaries@2x.png b/5-regular-expressions/03-regexp-character-classes/hello-java-boundaries@2x.png deleted file mode 100644 index 9ebf623b3a..0000000000 Binary files a/5-regular-expressions/03-regexp-character-classes/hello-java-boundaries@2x.png and /dev/null differ diff --git a/5-regular-expressions/03-regexp-character-classes/love-html5-classes.png b/5-regular-expressions/03-regexp-character-classes/love-html5-classes.png deleted file mode 100644 index b8ed77c0f5..0000000000 Binary files a/5-regular-expressions/03-regexp-character-classes/love-html5-classes.png and /dev/null differ diff --git a/5-regular-expressions/03-regexp-character-classes/love-html5-classes@2x.png b/5-regular-expressions/03-regexp-character-classes/love-html5-classes@2x.png deleted file mode 100644 index 9fb998b98b..0000000000 Binary files a/5-regular-expressions/03-regexp-character-classes/love-html5-classes@2x.png and /dev/null differ diff --git a/5-regular-expressions/04-regexp-escaping/article.md b/5-regular-expressions/04-regexp-escaping/article.md deleted file mode 100644 index 62c0c62ef7..0000000000 --- a/5-regular-expressions/04-regexp-escaping/article.md +++ /dev/null @@ -1,91 +0,0 @@ - -# エスケープ, 特殊文字 - -これまで見てきたように、バックスラッシュ `"\"` は文字クラスを表すのに使われます。なので、それは特別な文字です。 - -同様に他にも特殊文字があり、正規表現の中で特別な意味を持ちます。それらはよりパワフルな検索をするために使われます。 - -これがその完全なリストです: `pattern:[ \ ^ $ . | ? * + ( )`. - -覚えようとはしないでください -- それぞれを個別に見ていく際に、自然と覚えていくでしょう。 - -## エスケープ - -特殊文字を通常の文字として使用するには、バックスラッシュを付加します。 - -それは "文字をエスケープする" とも言われます。 - -例えば、ドット `pattern:'.'` を探す必要があるとします。正規表現でドットは "改行以外の任意の文字" を意味します。そのため、本当に "ドット" を意味する場合、前にバックスラッシュ `pattern:\.` を置きましょう。 - -```js run -alert( "Chapter 5.1".match(/\d\.\d/) ); // 5.1 -``` - -括弧も特殊文字なので、それらを探したい場合は `pattern:\(` を使う必要があります。下の例は文字列 `"g()"` を探します: - -```js run -alert( "function g()".match(/g\(\)/) ); // "g()" -``` - -バックスラッシュを探している場合は2つにします: - -```js run -alert( "1\\2".match(/\\/) ); // '\' -``` - -## スラッシュ - -スラッシュ記号 `'/'` は特殊文字ではありませんが、JavaScript では正規表現の開始と終了で使われる(`pattern:/...pattern.../`)ので、これもエスケープが必要です。 - -スラッシュ `'/'` の検索は次のようになります: - -```js run -alert( "/".match(/\//) ); // '/' -``` - -一方、別の `new RegExp` 構文ではエスケープする必要はありません: - -```js run -alert( "/".match(new RegExp("/")) ); // '/' -``` - -## new RegExp - -`new RegExp` で正規表現を作成している場合は、他にもエスケープが必要なものがいくつかあります。 - -例えばこれを見てください: - -```js run -let reg = new RegExp("\d\.\d"); - -alert( "Chapter 5.1".match(reg) ); // null -``` - -これは機能しません。なぜでしょう? - -理由は、文字列のエスケープルールによるものです。これを見てください: - -```js run -alert("\d\.\d"); // d.d -``` - -バックスラッシュは、文字列の中でエスケープしたり、`\n` のような文字列固有の特殊文字のために使われます。引用符はそれらを "消費" して解釈します。例えば: - -- `\n` -- は改行文字になります -- `\u1234` -- はそのようなコードをもつユニコード文字になります -- ...そして特殊な意味を持たないもの、`\d` や `\z` のようなものの場合には、バックスラッシュは単に除去されます。 - -したがって、`new RegExp` の呼び出しは、バックスラッシュのない文字列を取得します。 - -これを直すには、引用符で `\\` を `\` にするためにバックスラッシュを二重にする必要があります。: - -```js run -*!* -let regStr = "\\d\\.\\d"; -*/!* -alert(regStr); // \d\.\d (正しい) - -let reg = new RegExp(regStr); - -alert( "Chapter 5.1".match(reg) ); // 5.1 -``` diff --git a/5-regular-expressions/05-regexp-character-sets-and-ranges/article.md b/5-regular-expressions/05-regexp-character-sets-and-ranges/article.md deleted file mode 100644 index 6375c32098..0000000000 --- a/5-regular-expressions/05-regexp-character-sets-and-ranges/article.md +++ /dev/null @@ -1,116 +0,0 @@ -# セットと範囲 [...] - -角括弧 `[…]` 内の複数の文字または文字クラスは "指定された中の任意の文字を探す" ことを意味します。 - -[cut] - -## セット - -例えば、`pattern:[eao]` は3文字 `'a'`, `'e'`, または `'o'` のいずれかを意味します。 - -それは *セット* と呼ばれます。セットは通常の文字と併せて正規表現の中で使うことができます。: - -```js run -// [t or m], 次に "op" となる文字列を見つける -alert( "Mop top".match(/[tm]op/gi) ); // "Mop", "top" -``` - -セットには複数の文字がありますが、マッチした中での1文字に相当することに注意してください。 - -従って、下の例ではマッチするものはありません: - -```js run -// "V" に続き [o or i], その後 "la" となる文字列を見つける -alert( "Voila".match(/V[oi]la/) ); // null, マッチしない -``` - -パターンは次のように想定します: - -- `pattern:V`, -- 次に文字 `pattern:[oi]` の *1つ*, -- 次に `pattern:la`. - -なので、`match:Vola` もしくは `match:Vila` がマッチします。 - -## 範囲 - -角括弧は *文字の範囲* を含むこともあります。 - -例えば、`pattern:[a-z]` は `a` から `z` までの範囲の文字で、 `pattern:[0-5]` は `0` から `5` までの数字です。 - -下の例では、`x` に続いて2桁の数字または `A` から `F` までの文字を探しています: - -```js run -alert( "Exception 0xAF".match(/x[0-9A-F][0-9A-F]/g) ); // xAF -``` - -単語 `subject:Exception` で、部分文字列 `subject:xce` があることに注目していください。それはパターンにマッチしませんでした。理由はパターン `pattern:[0-9A-F]` が大文字であるのに対し、小文字だからです。 - -それも見つけたい場合は、`a-f` の範囲も追加します: `pattern:[0-9A-Fa-f]`。 `i` フラグも小文字を許容するでしょう。 - -**文字クラスは、特定の文字セットの短縮形です** - -例: - -- **\d** -- `pattern:[0-9]` と同じです, -- **\w** -- `pattern:[a-zA-Z0-9_]` と同じです, -- **\s** -- `pattern:[\t\n\v\f\r ]` に他のユニコードの空白文字を加えたものと同じです。 - -`[…]` の内側でも同様に文字クラスを使うことができます。 - -例えば、"twenty-third" のような言葉に対し、すべての単語文字またはダッシュにマッチさせたいとします。`pattern:\w` はダッシュを含まないため、`pattern:\w+` ではできません。しかし `pattern:[\w-]` とすることができます。 - -`pattern:[\s\S]` のように、可能性のあるすべての文字をカバーするためにクラスの組み合わせを使うこともできます。これは空白及び非空白 -- なので任意の文字に一致します。 ドット `"."` は改行以外の任意の文字にマッチするので、これはドットよりも広いです。 - -## 範囲を除外する - -通常の範囲に加えて、 `pattern:[^…]` のような、範囲を "除外" するものもあります。 - -それらは開始時にキャレット文字 `^` で指定され、*指定されたもの以外の文字* と一致します。 - -例: - -- `pattern:[^aeyo]` -- `'a'`, `'e'`, `'y'` または `'o'` を除く任意の文字. -- `pattern:[^0-9]` -- 数字以外の任意の文字, `\D` と同じです. -- `pattern:[^\s]` -- 任意の非空白文字, `\S` と同じです. - -下の例では、文字、数字または空白以外の文字を探します: - -```js run -alert( "alice15@gmail.com".match(/[^\d\sA-Z]/gi) ); // @ and . -``` - -## […] でエスケープしない - -通常、正確にドット文字を見つけたい場合、`pattern:\.` のようにエスケープが必要です。そしてバックスラッシュが必要な場合には、`pattern:\\` を使います。 - -角括弧で囲まれた大多数の特殊文字は、エスケープなしで使うことができます: - -- ドット `pattern:'.'`. -- プラス `pattern:'+'`. -- 丸括弧 `pattern:'( )'`. -- ダッシュ `pattern:'-'`, 先頭または末尾に(範囲を定義していない場合). -- キャレット `pattern:'^'`, 先頭でない場合(それは除外を意味する). -- そして、角括弧の開始 `pattern:'['`. - -つまり、角括弧を意味するものを除いて、すべての特殊文字が許可されます。 - -角括弧内のドット `"."` は単なるドットを意味します。パターン `pattern:[.,]` は1つの文字を探します: ドットまたはカンマです。 - -下の例では、正規表現 `pattern:[-().^+]` は `-().^+` の文字の1つを探します: - -```js run -// エスケープは必要ありません -let reg = /[-().^+]/g; - -alert( "1 + 2 - 3".match(reg) ); // +, - にマッチ -``` - -...しかし "念の為" エスケープすることを決めた場合にも特に害はありません: - -```js run -// すべてエスケープ -let reg = /[\-\(\)\.\^\+]/g; - -alert( "1 + 2 - 3".match(reg) ); // 同じく動作します: +, - -``` diff --git a/5-regular-expressions/06-regexp-unicode/article.md b/5-regular-expressions/06-regexp-unicode/article.md deleted file mode 100644 index aa9347fd92..0000000000 --- a/5-regular-expressions/06-regexp-unicode/article.md +++ /dev/null @@ -1,69 +0,0 @@ - -# ユニコードフラグ - -ユニコードフラグ `/.../u` はサロゲートペアの正しいサポートができるようになります。 - -サロゲートペアについては、チャプター <info:string> で説明されています。 - -簡単に思い出してみましょう。手短に言えば、通常の文字は2バイトでエンコードされています。それは最大で 65536 文字になります。しかし世界にはもっと多くの文字があります。 - -そのため、`𝒳` (数学的な X)や `😄` (スマイル)のような特定の希少な文字は4バイトでエンコードされています。 - -これは比較のためのユニコード値です: - -| Character | Unicode | Bytes | -|------------|---------|--------| -| `a` | 0x0061 | 2 | -| `≈` | 0x2248 | 2 | -|`𝒳`| 0x1d4b3 | 4 | -|`𝒴`| 0x1d4b4 | 4 | -|`😄`| 0x1f604 | 4 | - -したがって、`a` や `≈` と言った文字は 2バイトを占め、珍しいものは4バイトになります。 - -ユニコードは、4バイト文字がそれ全体でのみ意味を持つように作られています。 - -昔は JavaScript はそのことを知らなかったので、多くの文字列メソッドにはまだ問題があります。例えば、`length` はそれらを2つの文字であると考えます: - -```js run -alert('😄'.length); // 2 -alert('𝒳'.length); // 2 -``` - -...ですが、1文字にしか見えませんよね? ポイントは `length` は4バイトを2つの2バイト文字として扱うということです。それらは併せてでしか考えられない(いわゆる "サロゲートペア")ため、正しくありません。 - -通常、正規表現も2つの2バイト文字として "長い文字" を扱います。 - -これはおかしな結果に繋がります。例えば `subject:𝒳` という文字列で `pattern:[𝒳𝒴]` を見つけようとしてみましょう。: - -```js run -alert( '𝒳'.match(/[𝒳𝒴]/) ); // おかしな結果 -``` - -デフォルトでは正規表現のエンジンはサロゲートペアを理解しないため、結果は間違っています。`[𝒳𝒴]` は2つではなく、4つの文字(`𝒳` の左半分 `(1)`, `𝒳` の右半分 `(2)`, `𝒴` の左半分 `(3)`, `𝒴` の右半分 `(4)`) と考えます。 - -なので、全体ではなく文字列 `𝒳` で `𝒳` の左半分を見つけます。 - -つまり、検索は `'12'.match(/[1234]/)` のように動作します -- `1` が返ります(`𝒳` の左半分)。 - -`/.../u` フラグはそれを直します。正規表現エンジンでサロゲートペアを利用可能にするので結果は正しくなります: - -```js run -alert( '𝒳'.match(/[𝒳𝒴]/u) ); // 𝒳 -``` - -フラグを忘れた場合、エラーが起きる場合があります: - -```js run -'𝒳'.match(/[𝒳-𝒴]/); // SyntaxError: invalid range in character class -``` - -ここでは、正規表現 `[𝒳-𝒴]` は `[12-34]` と扱われます(`2` は `𝒳` の右部分、`3` は `𝒴` の左部分)。そしてその2つの半分 `2` と `3` の間の範囲は認められません。 - -フラグを使うと正しく動作します: - -```js run -alert( '𝒴'.match(/[𝒳-𝒵]/u) ); // 𝒴 -``` - -最後に、サロゲートペアを扱わなければフラグは何もしないことに留意しましょう。しかし、現在の世界では、しばしばそれらに出くわします。 diff --git a/5-regular-expressions/07-regexp-quantifiers/article.md b/5-regular-expressions/07-regexp-quantifiers/article.md deleted file mode 100644 index b016ba8075..0000000000 --- a/5-regular-expressions/07-regexp-quantifiers/article.md +++ /dev/null @@ -1,133 +0,0 @@ -# 量指定子 +, *, ? と {n} - -`+7(903)-123-45-67` という文字列があり、すべての文字を見つけたいとします。しかし、以前とは違い、数字だけでなく完全な数字が欲しいです: `7, 903, 123, 45, 67`。 - -数字は1つ以上の `\d` の連続です。どれだけ必要かを示す手段は *量指定子* と呼ばれます。 - -## 量指定子 {n} - -最も明白な量指定子は、波括弧の数字です: `pattern:{n}`。量指定子は文字(または文字クラスなど)の後に置かれ、正確にどれだか必要かを指定します。 - -また、高度なフォームも持っています。ここでその例を挙げます: - -正確なカウント: `{5}` -: `pattern:\d{5}` は正確に5桁であることを示し、 `pattern:\d\d\d\d\d` と同じです。 - - 下の例では 5桁の数値を探します: - - ```js run - alert( "I'm 12345 years old".match(/\d{5}/) ); // "12345" - ``` - - `\b` を追加してより長い数字を除外することもできます: `pattern:\b\d{5}\b`. - -from-to のでカウント: `{3,5}` -: 3桁 から 5桁の数値を探すには、波括弧の中に制限を入れることができます: `pattern:\d{3,5}` - - ```js run - alert( "I'm not 12, but 1234 years old".match(/\d{3,5}/) ); // "1234" - ``` - - 上限を省略することができます。正規表現 `pattern:\d{3,}` は`3` 桁以上の数字を探します: - - ```js run - alert( "I'm not 12, but 345678 years old".match(/\d{3,}/) ); // "345678" - ``` - -文字列 `+7(903)-123-45-67` のケースでは、私たちは数値が必要です: 連続した1桁以上の数字です。つまり `pattern:\d{1,}` です: - -```js run -let str = "+7(903)-123-45-67"; - -let numbers = str.match(/\d{1,}/g); - -alert(numbers); // 7,903,123,45,67 -``` - -## 簡略表記 - -もっとも頻繁に必要とされる量指定子には簡略表記があります: - -`+` -: "1つ以上" を意味し、`{1,}` と同じです。 - - 例えば、`pattern:\d+` は数字を探します: - - ```js run - let str = "+7(903)-123-45-67"; - - alert( str.match(/\d+/g) ); // 7,903,123,45,67 - ``` - -`?` -: "0 か 1" を意味し、`{0,1}` と同じです。つまりシンボルをオプションにします。 - - 例えば、パターン `pattern:ou?r` は `match:o` に続く 0 または 1つの `match:u`、そして続けて `match:r` です。 - - なので、単語 `subject:color` では `match:or` を見つけ、`subject:colour` では `match:our` を見つけます。: - - ```js run - let str = "Should I write color or colour?"; - - alert( str.match(/colou?r/g) ); // color, colour - ``` - -`*` -: "0 以上" を意味し、`{0,}` と同じです。つまり、文字は任意の数繰り返されるか、存在しない可能性があります。 - - 下の例は、ゼロが任意の数続く数字を探します: - - ```js run - alert( "100 10 1".match(/\d0*/g) ); // 100, 10, 1 - ``` - - `'+'` (1つ以上) と比較すると: - - ```js run - alert( "100 10 1".match(/\d0+/g) ); // 100, 10 - ``` - -## より多くの例 - -量指定子はとてもよく使われます。これらは複雑な正規表現でのメインの "ビルディングブロック" の1つなので、より多くの例を見てみましょう。 - -正規表現 "少数" (浮動小数点を持つ数値): `pattern:\d+\.\d+` -: 実演: - ```js run - alert( "0 1 12.345 7890".match(/\d+\.\d+/g) ); // 12.345 - ``` - -正規表現 `<span>` や `<p>` のような "属性なしの HTML の開始タグ": `pattern:/<[a-z]+>/i` -: 実演: - - ```js run - alert( "<body> ... </body>".match(/<[a-z]+>/gi) ); // <body> - ``` - - 文字 `pattern:'<'` に続けて1つ以上の英語文字、その後 `pattern:'>'` です - -正規表現 "属性なしの HTML の開始タグ" (改良版): `pattern:/<[a-z][a-z0-9]*>/i` -: より良い正規表現: 標準によれば、HTML タグ名は `<h1>` のように先頭以外の任意の場所に数字を許可します。 - - ```js run - alert( "<h1>Hi!</h1>".match(/<[a-z][a-z0-9]*>/gi) ); // <h1> - ``` - -正規表現 "属性なしの HTML の開始、終了タグ": `pattern:/<\/?[a-z][a-z0-9]*>/i` -: タグの前に任意のスラッシュ `pattern:/?` を追加しています。バックスラッシュでエスケープが必要です。そうしない場合、JavaScript はパターンの終わりだと認識するでしょう。 - - ```js run - alert( "<h1>Hi!</h1>".match(/<\/?[a-z][a-z0-9]*>/gi) ); // <h1>, </h1> - ``` - -```smart header="より精密、はより複雑、を意味します" -これらの例から1つの共通するルールが見えてきます: 正規表現がより詳細/精密になるほど、パターンはより長く複雑になります。 - -例えば、HTMLタグはより簡単な正規表現が使えます: `pattern:<\w+>`. - -なぜなら、`pattern:\w` は任意の英語文字または数字または `'_'` を意味するためです。また正規表現は例えば `match:<_>` のような非タグにもマッチします。しかし `pattern:<[a-z][a-z0-9]*>` よりもずっと簡単です。 - -私たちは `pattern:<\w+>` で良いですか?それとも `pattern:<[a-z][a-z0-9]*>` が必要ですか? - -実際にはどちらもパターンも許容されます。"余分な" マッチや他の手段でフィルタするのが難しいかどうかという点に対してどれだけ寛容であるかによります。 -``` diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/article.md b/5-regular-expressions/08-regexp-greedy-and-lazy/article.md deleted file mode 100644 index 157e0a0e4b..0000000000 --- a/5-regular-expressions/08-regexp-greedy-and-lazy/article.md +++ /dev/null @@ -1,309 +0,0 @@ -# 貪欲と怠惰な量指定子 - -量指定子は一見すると非常に簡単ですが、実際には扱いにくいです。 - -`pattern:/\d+/` よりも複雑なものを探す場合、検索がどのように上手く動作しているのかを理解する必要があります。 - -[cut] - -例として、次のタスクをやってみましょう。 - -テキストがあり、すべての引用符 `"..."` をギルメットマーク `«...»` に置き換える必要があります。それらは多くの国でタイポグラフィとして好まれています。 - -例えば: `"Hello, world"` は `«Hello, world»` になります。 - -国によっては、`„Witam, świat!”` (ポーランド語)や `「你好,世界」` (中国語) の引用符を好みます。異なるロケールでは、異なる置換を選ぶ可能性がありますが、すべて同じように動作するので、まずは `«...»` で始めましょう。 - -置換するためには、まずすべての引用符で囲まれた部分文字列を見つける必要があります。 - -正規表現はこのようになります: `pattern:/".+"/g`。つまり: 引用符に続いて1つ以上の文字が続き、その後に別の引用符が続きます。 - -...しかし、それを適用しようとすると、たとえ次のような単純なケースであっても... - -```js run -let reg = /".+"/g; - -let str = 'a "witch" and her "broom" is one'; - -alert( str.match(reg) ); // "witch" and her "broom" -``` - -...意図通りに動作していないことが分かります! - -`match:"witch"` と `match:"broom"`, 2つのマッチを見つける代わりに、`match:"witch" and her "broom"` 1つを見つけます。 - -それは、"貪欲は諸悪の根源" と表現することができます。 - -## 貪欲(欲張り/最大量)検索 - -マッチを見つけるために、正規表現エンジンは次のアルゴリズムを使います: - -- 文字列内のすべての位置で - - その位置でパターンをマッチさせます - - マッチしない場合は次の位置に移動します。 - -これらの一般的な言葉では正規表現が失敗する理由が明白でないため、パターン -`pattern:".+"` に対して検索がどのように機能するかを詳しく見ていきましょう。 - -1. 最初のパターン文字は引用符 `pattern:"` です。 - - 正規表現エンジンは、ソース文字列 `subject:a "witch" and her "broom" is one` のゼロ位置でそのパターンを見つけようとしますが、そこは `subject:a` なので、すぐには一致しません。 - - 次に進みます: ソース文字列の次の位置に移動し、そこで最初のパターン文字を見つけようとします。そして3番目の位置で引用符を見つけます。: - - ![](witch_greedy1.png) - -2. 引用符が検出され、次にエンジンはパターン残り部分のマッチを見つけようとします。ソース文字列の残りの部分が `pattern:.+"` に従っているかを確かめます。 - - 我々のケースでは、次のパターン文字は `pattern:.` (ドット)です。それは "改行以外の任意の文字" を意味するので、次の文字 `match:'w'` にフィットします: - - ![](witch_greedy2.png) - -3. 次に、量指定子 `pattern:.+` なのでドットを繰り返します。正規表現エンジンは可能な限り文字を1つずつ取り込み、マッチを作成します。 - - ...いつ不可能になるでしょう?すべての文字はドットにマッチするので、文字列の最後に到達したときにだけ停止します。: - - ![](witch_greedy3.png) - -4. いま、エンジンは `pattern:.+` の繰り返しを終了し、次のパターン文字を見つけようとします。それは引用符 `pattern:"` です。しかし、ここで問題あります: 文字列は終了したのでこれ以上文字はありません! - - 正規表現エンジンはあまりに多くの `pattern:.+` が引っかかったと理解し、*来た道を戻り* 始めます。 - - つまり、量指定子のマッチを1文字減らします。: - - ![](witch_greedy4.png) - - 今、`pattern:.+` は末尾の1文字前で終わり、残りのパターンをその位置からマッチさせようとします。 - - もしそこに引用符があれば終了しますが、最後の文字は `subject:'e'` なので一致しません。 - -5. ...なので、エンジンは `pattern:.+` の繰り返し回数をもう1文字減らします: - - ![](witch_greedy5.png) - - 引用符 `pattern:'"'` は `subject:'n'` に一致しません。 - -6. エンジンは戻り続けます: エンジンは残りのパターン(今回のケースでは `pattern:'"'`)にマッチするまで `pattern:'.'` の繰り返し回数を減らします。: - - ![](witch_greedy6.png) - -7. マッチが完了しました。 - -8. したがって、最初のマッチは `match:"witch" and her "broom"` です。さらなる検索は最初のマッチが終わったところから始まりますが、残りの文字列 `subject:is one` にはこれ以上引用はないため、それ以上の結果はありません。 - -これは恐らく我々が期待したものではありませんが、このように動作します。 - -**貪欲(Greedy)モード(デフォルト)では、量指定子は可能な限り繰り返されます。** - -正規表現エンジンは `pattern:.+` でできるだけ多くの文字を取得しようとし、その後1つずつ縮めていきます。 - -私たちのタスクでは、別のものが欲しいです。そのためのものとして、怠惰な/控えめな量指定子モードがあります。 - -## 怠惰(最短)モード - -量指定子の怠惰モードは貪欲モードとは逆です。それは "最小限の回数だけ繰り返す" を意味します。 - -これを有効にするには量指定子の後に疑問符 `pattern:'?'` を置き、`pattern:'?'` に対して `pattern:*?` や `pattern:+?` または `pattern:??`になるようにします。 - -よりはっきりさせる為に: 通常、疑問符マーク `pattern:?` はそれ自身が量指定子(0か1)ですが、*別の量指定子(または自身も)* の後に追加された場合、別の意味を持ちます -- マッチングのモードを貪欲から怠惰に切り替えます。 - -正規表現 `pattern:/".+?"/g` は期待通りに動作します: これは `match:"witch"` と `match:"broom"` を見つけます。: - -```js run -let reg = /".+?"/g; - -let str = 'a "witch" and her "broom" is one'; - -alert( str.match(reg) ); // witch, broom -``` - -変更をよりはっきり理解するために、検索をステップ毎にトレースしてみましょう。 - -1. 最初のステップは同じです: 3番目の位置でパターンの開始 `pattern:'"'` を見つけます。: - - ![](witch_greedy1.png) - -2. 次のステップも似ています: エンジンはドット `pattern:'.'` に対するマッチを見つけます: - - ![](witch_greedy2.png) - -3. ここから検索は異なります。`pattern:+?` は怠惰モードなので、エンジンはもう一度マッチさせようとはせず、パターンの残り部分 `pattern:'"'` とマッチさせようとします: - - ![](witch_lazy3.png) - - もしそこに引用符があれば、検索は終わっていましたが、`'i'` なのでマッチしません。 -4. 次に、正規表現エンジンはドットの繰り返し回数を増やし、もう一度試みます。: - - ![](witch_lazy4.png) - - 再び失敗です。その後、繰り返し回数は何度も増えていきます... -5. ...パターンの残り部分への一致が見つかるまで繰り返されます: - - ![](witch_lazy5.png) - -6. 次の検索は現在のマッチの終わりから始まり、もう1つ結果が得られます: - - ![](witch_lazy6.png) - -この例では、`pattern:+?` に対して怠惰モードがどのように動作するかを見てきました。量指定子 `pattern:+?` と `pattern:??` は同様の方法で動作します -- 残りのパターンが指定された位置で一致しない場合のみ、正規表現エンジンは繰り返し回数を増やします。 - -**怠惰は `?` をつけた量指定子に対してのみ有効です。** - -他の量指定子は依然として貪欲です。 - -例: - -```js run -alert( "123 456".match(/\d+ \d+?/g) ); // 123 4 -``` - -1. パターン `pattern:\d+` はできるだけ多くマッチさせようとし(貪欲モード)、`match:123` を見つけ停止します。なぜなら次の文字は空白 `pattern:' '` だからです。 -2. 次にパターンに空白があるので、それがマッチします。 -3. 続いて `pattern:\d+?` です。量指定子は怠惰モードなので、1桁の `match:4` を見つけ、残りのパターンがそこからマッチするかをチェックします。 - - ...しかしパターンは `pattern:\d+?` で終わりです。 - - 怠惰モードは必要がないので何も繰り返しません。パターンは終了したので、やることは終わりました。マッチしたのは `match:123 4` です。 -4. 次の検索は文字 `5` から開始します。 - -```smart header="最適化" -現代の正規表現エンジンはより高速に動作するために内部のアルゴリズムを最適化します。なので、実際には説明したアルゴリズムとは少し異なる動作をする場合があります。 - -しかし、正規表現がどのように動作するかを理解したり、正規表現を構築するのにそれらを知る必要はありません。それらは物事を最適化するために内部でのみ使用されます。 - -複雑な正規表現は最適化が難しいので、検索は説明した通りに正確に動作します。 -``` - -## 代替のアプローチ - -正規表現では、同じことをする方法が複数あることがよくあります。 - -我々のケースでは、`pattern:"[^"]+"` を使うことで、怠惰モードなしで引用符で囲まれた文字列を見つけることができます。: - -```js run -let reg = /"[^"]+"/g; - -let str = 'a "witch" and her "broom" is one'; - -alert( str.match(reg) ); // witch, broom -``` - -正規表現 `pattern:"[^"]+"` は正しい結果を返します。なぜなら、引用符 `pattern:'"'` に続けて1つ以上の非引用符 `pattern:[^"]` 、その後引用符を閉じるというパターンを探すからです。 - -正規表現エンジンが `pattern:[^"]+` を探す際、引用符閉じに出会うと繰り返しをやめ、検索が終わります。 - -このロジックは怠惰量指定子を置き換えるものではないことに注意してください! - -私たちはいずれかが必要なときがあります。 - -怠惰量指定子が失敗しこのバリアントが正しく動作するもう1つの例を見てみましょう。 - -例えば、任意の `href` をもつ形式 `<a href="..." class="doc">` のリンクを取得したいとします。 - -どちらの正規表現を使うべきでしょう? - -最初のアイデアは: `pattern:/<a href=".*" class="doc">/g` です。 - -確認してみましょう: -```js run -let str = '...<a href="link" class="doc">...'; -let reg = /<a href=".*" class="doc">/g; - -// 動作します! -alert( str.match(reg) ); // <a href="link" class="doc"> -``` - -...しかし、仮にテキスト中にもっとリンクがあるとどうなるでしょう? - -```js run -let str = '...<a href="link1" class="doc">... <a href="link2" class="doc">...'; -let reg = /<a href=".*" class="doc">/g; - -// Whoops! 1つのマッチに2つのリンクがあります! -alert( str.match(reg) ); // <a href="link1" class="doc">... <a href="link2" class="doc"> -``` - -今の結果は、私たちの "魔女" の例と同じ理由で間違っています。量指定子 `pattern:.*` は文字を多く取り過ぎました。 - -一致はこのように見えます: - -```html -<a href="....................................." class="doc"> -<a href="link1" class="doc">... <a href="link2" class="doc"> -``` - -量指定子 `pattern:.*?` を怠惰にすることでパターンを修正しましょう: - -```js run -let str = '...<a href="link1" class="doc">... <a href="link2" class="doc">...'; -let reg = /<a href=".*?" class="doc">/g; - -// 動作します! -alert( str.match(reg) ); // <a href="link1" class="doc">, <a href="link2" class="doc"> -``` - -これで機能し、2つのマッチが見つかります: - -```html -<a href="....." class="doc"> <a href="....." class="doc"> -<a href="link1" class="doc">... <a href="link2" class="doc"> -``` - -それがなぜ機能するか -- は上のすべての説明の後に明らかにした方がよいです。従って、詳細へは入らず、もう1つのテキストを試してみましょう: - -```js run -let str = '...<a href="link1" class="wrong">... <p style="" class="doc">...'; -let reg = /<a href=".*?" class="doc">/g; - -// 間違った一致です! -alert( str.match(reg) ); // <a href="link1" class="wrong">... <p style="" class="doc"> -``` - -正規表現がリンクだけでなく、`<p...>` を含むその後に続くテキストもマッチしていることがわかります。 - -なぜこのようなことが起こるのでしょうか? - -1. まず、正規表現はリンクの開始 `match:<a href="` を見つけます - -2. 次に `pattern:.*?` を探し、1文字を取ります。その後、パターンの残り部分にマッチするものがあるかをチェックし、もう1文字取ります... - - 量指定子 `pattern:.*?` は `match:class="doc">` に到達するまで文字を取ります。 - - ...それはどこで見つかるでしょう?テキストを見ると、`match:class="doc">` はリンクを越えた、タグ `<p>` の中にだけあることが分かります。 - -3. したがって、一致は次のようになります: - - ```html - <a href="..................................." class="doc"> - <a href="link1" class="wrong">... <p style="" class="doc"> - ``` - -そのため、怠惰はここでは動作しませんでした。 - -私たちは `<a href="...something..." class="doc">` を探すパターンが必要ですが、貪欲と怠惰、両方のバリアントに問題がありません。 - -正しいバリアントは次のようになります: `pattern:href="[^"]*"`。これは `href` 属性の中のすべての文字を取ります。それは最も近い引用符までであり、まさに私たちが必要なものです。 - -動作例: - -```js run -let str1 = '...<a href="link1" class="wrong">... <p style="" class="doc">...'; -let str2 = '...<a href="link1" class="doc">... <a href="link2" class="doc">...'; -let reg = /<a href="[^"]*" class="doc">/g; - -// 動作します! -alert( str1.match(reg) ); // null, マッチしません。これは正しいです。 -alert( str2.match(reg) ); // <a href="link1" class="doc">, <a href="link2" class="doc"> -``` - -## サマリ - -量指定子には2つの動作モードがあります: - -貪欲(Greedy) -: デフォルトでは、正規表現エンジンは可能な限り多く量指定子を繰り返そうとします。例えば、`pattern:\d+` は可能なすべての数字になります。(これ以上数字がない,または文字の終わり)でこれ以上繰り返せなくなると、パターンの残り部分のマッチを続けます。もし一致がない場合、繰り返しの数を減らし(バックトレース)、再度マッチを試みます。 - -怠惰(Lazy) -: 量指定子の後の疑問符記号 `pattern:?` で有効になります。正規表現エンジンは、量指定子の各繰り返しの前に残りのパターンのマッチを試みます。 - -見てきたように、怠惰モードは貪欲検索の "万能薬" ではありません。代替は除外をもつ "微調整された" 貪欲検索です。すぐにそれについてもっと例を見てみましょう。 diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy1.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy1.png deleted file mode 100644 index 5db6f17a82..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy1.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy1@2x.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy1@2x.png deleted file mode 100644 index 966e27aa85..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy1@2x.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy2.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy2.png deleted file mode 100644 index 9fdbe2343d..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy2.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy2@2x.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy2@2x.png deleted file mode 100644 index 91e6658246..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy2@2x.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy3.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy3.png deleted file mode 100644 index aeef75bd6b..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy3.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy3@2x.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy3@2x.png deleted file mode 100644 index a8c62e60d7..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy3@2x.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy4.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy4.png deleted file mode 100644 index c18ab43ecb..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy4.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy4@2x.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy4@2x.png deleted file mode 100644 index 823a371710..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy4@2x.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy5.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy5.png deleted file mode 100644 index 12442d2bdc..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy5.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy5@2x.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy5@2x.png deleted file mode 100644 index 29d3cf0af7..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy5@2x.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy6.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy6.png deleted file mode 100644 index 3ed72b25dc..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy6.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy6@2x.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy6@2x.png deleted file mode 100644 index 4833e67699..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_greedy6@2x.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy3.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy3.png deleted file mode 100644 index 1e0ce4178f..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy3.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy3@2x.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy3@2x.png deleted file mode 100644 index 1e378468dc..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy3@2x.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy4.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy4.png deleted file mode 100644 index 478834eec1..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy4.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy4@2x.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy4@2x.png deleted file mode 100644 index 495a2f2430..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy4@2x.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy5.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy5.png deleted file mode 100644 index e50543675b..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy5.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy5@2x.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy5@2x.png deleted file mode 100644 index 870d463305..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy5@2x.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy6.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy6.png deleted file mode 100644 index 66728501c4..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy6.png and /dev/null differ diff --git a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy6@2x.png b/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy6@2x.png deleted file mode 100644 index 4228e193b5..0000000000 Binary files a/5-regular-expressions/08-regexp-greedy-and-lazy/witch_lazy6@2x.png and /dev/null differ diff --git a/5-regular-expressions/09-regexp-groups/3-find-decimal-positive-numbers/solution.md b/5-regular-expressions/09-regexp-groups/3-find-decimal-positive-numbers/solution.md deleted file mode 100644 index 8c51b63053..0000000000 --- a/5-regular-expressions/09-regexp-groups/3-find-decimal-positive-numbers/solution.md +++ /dev/null @@ -1,16 +0,0 @@ - -整数値は `pattern:\d+`. - -小数点は: `pattern:\.\d+`. - -小数点は任意なので、量指定子 `pattern:'?'` をもつ括弧の中に置きましょう。 - -これで完成です: `pattern:\d+(\.\d+)?`: - -```js run -let reg = /\d+(\.\d+)?/g; - -let str = "1.5 0 12. 123.4."; - -alert( str.match(re) ); // 1.5, 0, 12, 123.4 -``` diff --git a/5-regular-expressions/09-regexp-groups/3-find-decimal-positive-numbers/task.md b/5-regular-expressions/09-regexp-groups/3-find-decimal-positive-numbers/task.md deleted file mode 100644 index 7e3390b70f..0000000000 --- a/5-regular-expressions/09-regexp-groups/3-find-decimal-positive-numbers/task.md +++ /dev/null @@ -1,12 +0,0 @@ -# 正数を見つける - -小数点のないものも含め正数を探す正規表現を作成してください。 - -使用した例: -```js -let reg = /your regexp/g; - -let str = "1.5 0 12. 123.4."; - -alert( str.match(reg) ); // 1.5, 0, 12, 123.4 -``` diff --git a/5-regular-expressions/09-regexp-groups/article.md b/5-regular-expressions/09-regexp-groups/article.md deleted file mode 100644 index a6bb0d20e3..0000000000 --- a/5-regular-expressions/09-regexp-groups/article.md +++ /dev/null @@ -1,172 +0,0 @@ -# キャプチャグループ - -パターンの一部を丸括弧 `pattern:(...)`で囲むことができます。これは "キャプチャグループ" と呼ばれています。 - -これには2つの効果があります: - -1. [String#match](mdn:js/String/match) または [RegExp#exec](mdn:/RegExp/exec) メソッドを利用したとき、マッチした部分を別々の配列アイテムに置くことができます。 -2. 丸括弧の後の量指定子を置いた場合、最後の文字ではなく全体に丸括弧が適用されます。 - -[cut] - -## 例 - -下の例では、パターン `pattern:(go)+` は1つ以上の `match:'go'` を見つけます: - -```js run -alert( 'Gogogo now!'.match(/(go)+/i) ); // "Gogogo" -``` - -括弧なしだと、パターン `pattern:/go+/` は `subject:g` と、それに続けて1回以上の `subject:o` の繰り返しを意味します、例えば、`match:goooo` や `match:gooooooooo` です。 - -括弧は単語 `pattern:(go)` をグループ化します。 - -もっと複雑なもの -- メールアドレスにマッチする正規表現を作りましょう。 - -メールアドレスの例です: - -``` -my@mail.com -john.smith@site.com.uk -``` - -パターンは: `pattern:[-.\w]+@([\w-]+\.)+[\w-]{2,20}` です。 - -- `@` の前の最初のパートは、`match:john.smith` のように単語的な文字、ドットとダッシュを含みます `pattern:[-.\w]+`。 -- 次は `pattern:@` です。 -- そして、ドメインです。2階層のドメイン `site.com` かもしれないし、`host.site.com.uk` のようにサブドメインを持つ可能性があります。私たちは、`match:mail.` または `match:site.com.` のようなサブドメインに対して、"後ろにドットが続く単語" の1回以上の繰り返しをし、その後 `match:.com` や `match:.uk` のような最後の部分に対する "単語" としてマッチさせることができます。 - - "ドットが続く単語" は `pattern:(\w+\.)+` (繰り返し) です。最後の言葉は末尾にドットは持たないので、単に `\w{2,20}` になります。量指定子 `pattern:{2,20}` は長さを制限します。`.uk`, `.com` や `.museum` のようなドメインのゾーンは 20文字を超えることはできないためです。 - - なので、ドメインパターンは `pattern:(\w+\.)+\w{2,20}` になります。さらに、ドメインはダッシュも許容するので `\w` を `[\w-]` に置き換えます。これで最終的な結果を得ました。 - -この正規表現は完璧ではありませんが、たいていの場合動作します。これはパターンが短く、誤りや時折起きる間違いを修正するのには十分です。 - -例えば、ここでは文字列中のすべてのメールアドレスを見つけます: - -```js run -let reg = /[-.\w]+@([\w-]+\.)+[\w-]{2,20}/g; - -alert("my@mail.com @ his@site.com.uk".match(reg)); // my@mail.com,his@site.com.uk -``` - - -## 括弧の内容 - -丸括弧は左から右へ番号付けされます。検索エンジンはそれぞれの中身を覚えており、パターンまたは置換文字列の中で内容を参照することができます。 - -例えば、(簡略化された) パターン `pattern:<.*?>` を使って HTML タグを見つけます。通常、結果の後に何かをしたいでしょう。 - -`<...>` の中身を丸括弧で囲むと、次のようにしてアクセスすることができます: - -```js run -let str = '<h1>Hello, world!</h1>'; -let reg = /<(.*?)>/; - -alert( str.match(reg) ); // Array: ["<h1>", "h1"] -``` - -正規表現が `pattern:/.../g` フラグを持っていないときだけ、[String#match](mdn:js/String/match) の呼び出しはグループを返します。 - -グループとのすべてのマッチが必要な場合、<info:regexp-methods> で説明したように [RegExp#exec](mdn:js/RegExp/exec) メソッドを使います。: - -```js run -let str = '<h1>Hello, world!</h1>'; - -// 2つマッチします: 開始 <h1> と閉じ </h1> タグです -let reg = /<(.*?)>/g; - -let match; - -while (match = reg.exec(str)) { - // 最初のマッチを表示: <h1>,h1 - // 次のマッチを表示: </h1>,/h1 - alert(match); -} -``` - -ここでは `pattern:<(.*?)>` で2つのマッチがあり、それぞれが完全なマッチとグループの配列です。 - -## ネストされたグループ - -括弧はネストすることができます。この場合も数字は左から右です。 - -例えば、`subject:<span class="my">` でタグを探すとき、次の内容に興味を持つかもしれません: - -1. タグ全体のコンテンツ: `match:span class="my"`. -2. タグの名前: `match:span`. -3. タグの属性: `match:class="my"`. - -これらのための括弧を追加しましょう: - -```js run -let str = '<span class="my">'; - -let reg = /<(([a-z]+)\s*([^>]*))>/; - -let result = str.match(reg); -alert(result); // <span class="my">, span class="my", span, class="my" -``` - -グループは次のようになります: - -![](regexp-nested-groups.png) - -`result` の先頭のインデックスは常に完全なマッチです。 - -次にグループで、左から右に番号付けされています。ここでは `result[1]` はタグコンテンツ全体が囲まれています。 - -次の `result[2]` は, 2つ目の開始 `result[2]` から対応する `pattern:)` までのグループ -- タグ名で、その後スペースはグループ化していませんが、`result[3]` で属性をグループ化しています。 - -**もしグループが任意であり、マッチに存在しない場合、対応する `result` のインデックスは存在します(が、 `undefined` です)。** - -例えば、正規表現 `pattern:a(z)?(c)?` を考えてみましょう。これは `"a"` に任意の `"z"`が続き, それに任意の `"c"` が続くパターンを探します。 - -もし1文字 `subject:a` に対して実行すると、結果はこのようになります: - -```js run -let match = 'a'.match(/a(z)?(c)?/); - -alert( match.length ); // 3 -alert( match[0] ); // a (マッチ全体) -alert( match[1] ); // undefined -alert( match[2] ); // undefined -``` - -配列は長さ `3` ですが、すべてのグループは空です。 - -そして、文字列 `subject:ack` の場合はより複雑なマッチになります: - -```js run -let match = 'ack'.match(/a(z)?(c)?/) - -alert( match.length ); // 3 -alert( match[0] ); // ac (マッチ全体) -alert( match[1] ); // undefined, (z)? がないので。 -alert( match[2] ); // c -``` - -配列の長さは不変で `3` です。しかしグループ `pattern:(z)?` は無いので、結果は `["ac", undefined, "c"]` になります。 - -## ? を使用した非キャプチャグループ: - -量指定子を正しく適用するために括弧が必要ですが、配列にそれらの内容が必要でない場合があります。 - -先頭に `pattern:?:` を追加するとグループを除外することができます。 - -例えば、`pattern:(go)+` を見つけたいですが、別の配列アイテムにその内容 (`go`) を覚えたくない場合、`pattern:(?:go)+` と書くことができます。 - -下の例では、`results` の配列の別の要素として名前 "John" だけを取得します。: - -```js run -let str = "Gogo John!"; -*!* -// キャプチャから Gogo を除外します -let reg = /(?:go)+ (\w+)/i; -*/!* - -let result = str.match(reg); - -alert( result.length ); // 2 -alert( result[1] ); // John -``` diff --git a/5-regular-expressions/09-regexp-groups/regexp-nested-groups.png b/5-regular-expressions/09-regexp-groups/regexp-nested-groups.png deleted file mode 100644 index 4434b7be03..0000000000 Binary files a/5-regular-expressions/09-regexp-groups/regexp-nested-groups.png and /dev/null differ diff --git a/5-regular-expressions/09-regexp-groups/regexp-nested-groups@2x.png b/5-regular-expressions/09-regexp-groups/regexp-nested-groups@2x.png deleted file mode 100644 index 2721e79d37..0000000000 Binary files a/5-regular-expressions/09-regexp-groups/regexp-nested-groups@2x.png and /dev/null differ diff --git a/5-regular-expressions/10-regexp-backreferences/article.md b/5-regular-expressions/10-regexp-backreferences/article.md deleted file mode 100644 index fe7f3374ad..0000000000 --- a/5-regular-expressions/10-regexp-backreferences/article.md +++ /dev/null @@ -1,62 +0,0 @@ -# 後方参照: \n と $n - -キャプチャグループは結果だけでなく、置換文字列やパターンの中でもアクセスすることができます。 - -[cut] - -## 置換でのグループ: $n - -`replace` メソッドを使用しているとき、`$n` を使って置換文字列中の n 番目のグループにアクセスすることができます。 - -例: - -```js run -let name = "John Smith"; - -name = name.replace(/(\w+) (\w+)/i, *!*"$2, $1"*/!*); -alert( name ); // Smith, John -``` - -ここで置換文字列中の `pattern:$1` は "ここに最初のグループの内容を置き換える" を意味し、`pattern:$2` は "ここは2番目のグループに置き換える" を意味します。 - -置換文字列内のグループを参照することで、置換の中で既存のテキストを再利用することが可能です。 - -## パターンでのグループ: \n - -グループは `\n` を使うことでmパターンの中で参照することができます。 - -より明白にするために、次のことを考えてみましょう。私たちは引用符で囲まれた文字列を見つける必要があります: シングルクォート `subject:'...'` またはダブルクォート `subject:"..."` -- 両方のバリアントがマッチする必要があります。 - -どうやってそれらを探しますか? - -パターン中に2種類の引用符をおきます: `pattern:['"](.*?)['"]`。これは `match:"..."` and `match:'...'` のような文字列を見つけますが、`subject:"She's the one!"` の文字列のように、ある引用符が別の引用符の中に登場した時、正しくないマッチになります。: - -```js run -let str = "He said: \"She's the one!\"."; - -let reg = /['"](.*?)['"]/g; - -// 結果は期待したものではありません -alert( str.match(reg) ); // "She' -``` - -ご覧の通り、パターンは開始の引用符 `match:"` を見つけ、その後テキストは他の引用符 `match: '` まで怠惰で消費され、マッチを閉じます。 - -パターンが開始引用符と同じ閉じ引用符を探すようにするために、それをグループ化して後方参照を使用しましょう: - -```js run -let str = "He said: \"She's the one!\"."; - -let reg = /(['"])(.*?)\1/g; - -alert( str.match(reg) ); // "She's the one!" -``` - -これですべて正しいです! 正規表現エンジンは最初の引用符 `pattern:(['"])` を見つけ、`pattern:(...)` の中身を覚えます。それは最初のキャプチャグループです。 - -さらにパターン `pattern:\1` は "最初のグループと同じテキストを見つける" ことを意味します。 - -注意してください: - -- 置換文字列の中でグループを参照するには -- `$1` を使います。一方、パターンは -- バックスラッシュ `\1` です。 -- グループの中で `?:` を使用すると、それを参照することはできません。キャプチャ `(?:...)` により除外されたグループはエンジンによって記憶されません。 diff --git a/5-regular-expressions/11-regexp-alternation/article.md b/5-regular-expressions/11-regexp-alternation/article.md deleted file mode 100644 index a1a7a49bd8..0000000000 --- a/5-regular-expressions/11-regexp-alternation/article.md +++ /dev/null @@ -1,72 +0,0 @@ -# 論理和指定子(Alternation) (OR) | - -論理和指定子は、実際には単純な "OR" である正規表現の用語です。 - -正規表現では、縦線の文字 `pattern:|` で表現されます。 - -[cut] - -例えば、プログラム言語を探す必要があるとします: HTML, PHP, Java, または JavaScript です。 - -対応する正規表現は次の通りです: `pattern:html|php|java(script)?`. - -利用例: - -```js run -let reg = /html|php|css|java(script)?/gi; - -let str = "First HTML appeared, then CSS, then JavaScript"; - -alert( str.match(reg) ); // 'HTML', 'CSS', 'JavaScript' -``` - -私たちは既に同様のことを知っています -- 角括弧です。`pattern:gr[ae]y` は `match:gray` または `match:grey` にマッチするように、複数の文字から選択することができます。 - -論理和指定子は文字レベルで動作するのではなく、式のレベルで動作します。正規表現 `pattern:A|B|C` は `A`, `B` または `C` の式のいずれか、を意味します。 - -例: - -- `pattern:gr(a|e)y` はまさに `pattern:gr[ae]y` と同じ意味です。 -- `pattern:gra|ey` は "gra" または "ey" を意味します。 - -論理和指定子では、パターンの一部を区切るには通常次のようにカッコで囲みます。: `pattern:before(XXX|YYY)after`. - -## 時間の正規表現 - -前のチャプターで、`12:00` のような形式 `hh:mm` の時間を探す正規表現を構築するタスクがありました。しかし、単純な `pattern:\d\d:\d\d` はあまりにも要領を得ません。これは `25:99` も時間として許容します。 - -どうすればより優れた正規表現を作れるでしょうか? - -その方法として、私たちはより慎重なマッチングを適用することができます: - -- 最初の数字は `0` か `1` で、その後に任意の数値が続く必要があります。 -- もしくは `2` でその後に `pattern:[0-3]` が続きます。 - -正規表現としてはこのようになります: `pattern:[01]\d|2[0-3]`. - -その後、コロンと分の部分を追加します。 - -分 は `0` から `59` までである必要があり、正規表現では最初の数字 `pattern:[0-5]` でその後に他の数字 `\d` が続くことを意味します。 - -パターンにそれらを引っ付けましょう: `pattern:[01]\d|2[0-3]:[0-5]\d`. - -ほとんど完了していますが、まだ問題があります。論理和指定子 `|` は `pattern:[01]\d` と `pattern:2[0-3]:[0-5]\d` の間です。これだと左右どちらかのパターンがマッチするすることになるので間違いです。 - -```js run -let reg = /[01]\d|2[0-3]:[0-5]\d/g; - -alert("12".match(reg)); // 12 ([01]\d にマッチ) -``` - -それはかなり明らかではありますが、正規表現で作業を開始するときには依然としてよく起きるミスです。 - -時間の部分 `[01]\d` OR `2[0-3]` へ正確に論理和指定子を適用するために括弧が必要です。 - - -正しいバリアントです: - -```js run -let reg = /([01]\d|2[0-3]):[0-5]\d/g; - -alert("00:00 10:10 23:59 25:99 1:2".match(reg)); // 00:00,10:10,23:59 -``` diff --git a/5-regular-expressions/12-regexp-anchors/2-test-mac/solution.md b/5-regular-expressions/12-regexp-anchors/2-test-mac/solution.md deleted file mode 100644 index 34030ac007..0000000000 --- a/5-regular-expressions/12-regexp-anchors/2-test-mac/solution.md +++ /dev/null @@ -1,21 +0,0 @@ -2桁の16進数は `pattern:[0-9a-f]{2}` (`pattern:i` フラグが有効である想定)です。 - -数値 `NN`, そして続いて `:NN` が5回繰り返される必要があります。: - -正規表現は: `pattern:[0-9a-f]{2}(:[0-9a-f]{2}){5}` です。 - -次に、マッチはテキスト全体を捉える必要があります。: 先頭から始まり、末尾で終わりです。これは `pattern:^...$` でパターンをラップすることで実現できます。 - -最終的に: - -```js run -let reg = /^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}$/i; - -alert( reg.test('01:32:54:67:89:AB') ); // true - -alert( reg.test('0132546789AB') ); // false (コロンなし) - -alert( reg.test('01:32:54:67:89') ); // false (5 個, 6 個である必要があります) - -alert( reg.test('01:32:54:67:89:ZZ') ) // false (末尾が ZZ) -``` diff --git a/5-regular-expressions/12-regexp-anchors/2-test-mac/task.md b/5-regular-expressions/12-regexp-anchors/2-test-mac/task.md deleted file mode 100644 index 6c973abdaa..0000000000 --- a/5-regular-expressions/12-regexp-anchors/2-test-mac/task.md +++ /dev/null @@ -1,20 +0,0 @@ -# MAC アドレスをチェックする - -ネットワークインターフェースの [MAC アドレス](https://en.wikipedia.org/wiki/MAC_address) はコロンで区切られた6つの2桁の16進数で構成されています。 - -例: `subject:'01:32:54:67:89:AB'`. - -文字列が MAC アドレスかどうかをチェックする正規表現を書いてください。 - -使用方法: -```js -let reg = /your regexp/; - -alert( reg.test('01:32:54:67:89:AB') ); // true - -alert( reg.test('0132546789AB') ); // false (コロンなし) - -alert( reg.test('01:32:54:67:89') ); // false (5 個, 6 個である必要があります) - -alert( reg.test('01:32:54:67:89:ZZ') ) // false (末尾が ZZ) -``` diff --git a/5-regular-expressions/13-regexp-multiline-mode/article.md b/5-regular-expressions/13-regexp-multiline-mode/article.md deleted file mode 100644 index 04de3eb3e4..0000000000 --- a/5-regular-expressions/13-regexp-multiline-mode/article.md +++ /dev/null @@ -1,78 +0,0 @@ -# 複数行モード, フラグ "m" - -フラグ `pattern:/.../m` とすることで、複数行モードを有効にできます。 - -[cut] - -これは `pattern:^` と `pattern:$` の動作にのみ影響します。 - -複数行モードでは、文字列の始めと終わりだけでなく、行の始まりと終わりにもマッチします。 - -## 行の開始 ^ - -下の例では、テキストは複数行です。パターン `pattern:/^\d+/gm` はそれぞれの行の先頭から数字を取ります。: - -```js run -let str = `1st place: Winnie -2nd place: Piglet -33rd place: Eeyore`; - -*!* -alert( str.match(/^\d+/gm) ); // 1, 2, 33 -*/!* -``` - -`pattern:/.../m` フラグがない場合は、最初の数値だけがマッチします: - - -```js run -let str = `1st place: Winnie -2nd place: Piglet -33rd place: Eeyore`; - -*!* -alert( str.match(/^\d+/g) ); // 1 -*/!* -``` - -これは、デフォルトではキャレット `pattern:^` はテキストの先頭にのみマッチし、複数行モードでは -- 行の始まりがマッチするためです。 - -正規表現エンジンはテキストに沿って進み、`pattern:^` で始まる文字列を探し、見つけたとき -- パターン `pattern:\d+` の残りの部分のマッチを続けます。 - -## 行の終わり $ - -ドル記号 `pattern:$` も同様に振る舞います。 - -正規表現 `pattern:\w+$` は各行で最後の単語を見つけます。: - -```js run -let str = `1st place: Winnie -2nd place: Piglet -33rd place: Eeyore`; - -alert( str.match(/\w+$/gim) ); // Winnie,Piglet,Eeyore -``` - -フラグ `pattern:/.../m` がなければ、ドル `pattern:$` は文字列全体の終わりにのみマッチします。なので、最後の単語だけが見つかるでしょう。 - -## アンカー ^$ vs \n - -改行を見つけるには、`pattern:^` と `pattern:$` だけでなく、改行文字 `\n` を使うこともできます。 - -最初の違いは、アンカーとは異なり、文字 `\n` は改行文字を "消費" し、結果にそれを追加することです。 - -例えば、ここでは `pattern:$` の代わりに `\n` を使っています: - -```js run -let str = `1st place: Winnie -2nd place: Piglet -33rd place: Eeyore`; - -alert( str.match(/\w+\n/gim) ); // Winnie\n,Piglet\n -``` - -ここでは、すべてのマッチは単語に改行文字を加えたものです。 - -そして、もう1つの違いは -- 改行 `\n` は文字列の末尾にはマッチしないことです。そのため、`Eeyore` は上の例では見つかりませんでした。 - -したがって、通常はアンカーのほうが優れています。それらは我々が欲しいものにより近いです。 diff --git a/5-regular-expressions/14-regexp-lookahead/article.md b/5-regular-expressions/14-regexp-lookahead/article.md deleted file mode 100644 index d7f3cec92f..0000000000 --- a/5-regular-expressions/14-regexp-lookahead/article.md +++ /dev/null @@ -1,3 +0,0 @@ -# 先読み (進行中) - -この記事は作業中です。準備ができ次第公開予定です。 diff --git a/5-regular-expressions/15-regexp-infinite-backtracking-problem/article.md b/5-regular-expressions/15-regexp-infinite-backtracking-problem/article.md deleted file mode 100644 index 5dd4c446a8..0000000000 --- a/5-regular-expressions/15-regexp-infinite-backtracking-problem/article.md +++ /dev/null @@ -1,274 +0,0 @@ -# Infinite backtracking problem - -Some regular expressions are looking simple, but can execute veeeeeery long time, and even "hang" the JavaScript engine. - -Sooner or later most developers occasionally face such behavior. - -The typical situation -- a regular expression works fine sometimes, but for certain strings it "hangs" consuming 100% of CPU. - -That may even be a vulnerability. For instance, if JavaScript is on the server, and it uses regular expressions to process user data, then such an input may cause denial of service. The author personally saw and reported such vulnerabilities even for well-known and widely used programs. - -So the problem is definitely worth to deal with. - -[cut] - -## Example - -The plan will be like this: - -1. First we see the problem how it may occur. -2. Then we simplify the situation and see why it occurs. -3. Then we fix it. - -For instance let's consider searching tags in HTML. - -We want to find all tags, with or without attributes -- like `subject:<a href="..." class="doc" ...>`. We need the regexp to work reliably, because HTML comes from the internet and can be messy. - -In particular, we need it to match tags like `<a test="<>" href="#">` -- with `<` and `>` in attributes. That's allowed by [HTML standard](https://html.spec.whatwg.org/multipage/syntax.html#syntax-attributes). - -Now we can see that a simple regexp like `pattern:<[^>]+>` doesn't work, because it stops at the first `>`, and we need to ignore `<>` inside an attribute. - -```js run -// the match doesn't reach the end of the tag - wrong! -alert( '<a test="<>" href="#">'.match(/<[^>]+>/) ); // <a test="<> -``` - -We need the whole tag. - -To correctly handle such situations we need a more complex regular expression. It will have the form `pattern:<tag (key=value)*>`. - -In the regexp language that is: `pattern:<\w+(\s*\w+=(\w+|"[^"]*")\s*)*>`: - -1. `pattern:<\w+` -- is the tag start, -2. `pattern:(\s*\w+=(\w+|"[^"]*")\s*)*` -- is an arbitrary number of pairs `word=value`, where the value can be either a word `pattern:\w+` or a quoted string `pattern:"[^"]*"`. - -That doesn't yet support few details of HTML grammar, for instance strings in 'single' quotes, but they can be added later, so that's somewhat close to real life. For now we want the regexp to be simple. - -Let's try it in action: - -```js run -let reg = /<\w+(\s*\w+=(\w+|"[^"]*")\s*)*>/g; - -let str='...<a test="<>" href="#">... <b>...'; - -alert( str.match(reg) ); // <a test="<>" href="#">, <b> -``` - -Great, it works! It found both the long tag `match:<a test="<>" href="#">` and the short one `match:<b>`. - -Now let's see the problem. - -If you run the example below, it may hang the browser (or whatever JavaScript engine runs): - -```js run -let reg = /<\w+(\s*\w+=(\w+|"[^"]*")\s*)*>/g; - -let str = `<tag a=b a=b a=b a=b a=b a=b a=b a=b - a=b a=b a=b a=b a=b a=b a=b a=b a=b a=b a=b a=b a=b`; - -*!* -// The search will take a long long time -alert( str.match(reg) ); -*/!* -``` - -Some regexp engines can handle that search, but most of them don't. - -What's the matter? Why a simple regular expression on such a small string "hangs"? - -Let's simplify the situation by removing the tag and quoted strings. - -Here we look only for attributes: - -```js run -// only search for space-delimited attributes -let reg = /<(\s*\w+=\w+\s*)*>/g; - -let str = `<a=b a=b a=b a=b a=b a=b a=b a=b - a=b a=b a=b a=b a=b a=b a=b a=b a=b a=b a=b a=b a=b`; - -*!* -// the search will take a long, long time -alert( str.match(reg) ); -*/!* -``` - -The same problem persists. - -Here we end the demo of the problem and start looking into what's going on and why it hangs. - -## Backtracking - -To make an example even simpler, let's consider `pattern:(\d+)*$`. - -This regular expression also has the same problem. In most regexp engines that search takes a very long time (careful -- can hang): - -```js run -alert( '12345678901234567890123456789123456789z'.match(/(\d+)*$/) ); -``` - -So what's wrong with the regexp? - -First, one may notice that the regexp is a little bit strange. The quantifier `pattern:*` looks extraneous. If we want a number, we can use `pattern:\d+$`. - -Indeed, the regexp is artificial. But the reason why it is slow is the same as those we saw above. So let's understand it, and then return to the real-life examples. - -What happen during the search of `pattern:(\d+)*$` in the line `subject:123456789z`? - -1. First, the regexp engine tries to find a number `pattern:\d+`. The plus `pattern:+` is greedy by default, so it consumes all digits: - - ``` - \d+....... - (123456789)z - ``` -2. Then it tries to apply the star around the parentheses `pattern:(\d+)*`, but there are no more digits, so it the star doesn't give anything. - - Then the pattern has the string end anchor `pattern:$`, and in the text we have `subject:z`. - - ``` - X - \d+........$ - (123456789)z - ``` - - No match! -3. There's no match, so the greedy quantifier `pattern:+` decreases the count of repetitions (backtracks). - - Now `\d+` is not all digits, but all except the last one: - ``` - \d+....... - (12345678)9z - ``` -4. Now the engine tries to continue the search from the new position (`9`). - - The start `pattern:(\d+)*` can now be applied -- it gives the number `match:9`: - - ``` - - \d+.......\d+ - (12345678)(9)z - ``` - - The engine tries to match `$` again, but fails, because meets `subject:z`: - - ``` - X - \d+.......\d+ - (12345678)(9)z - ``` - - There's no match, so the engine will continue backtracking. -5. Now the first number `pattern:\d+` will have 7 digits, and the rest of the string `subject:89` becomes the second `pattern:\d+`: - - ``` - X - \d+......\d+ - (1234567)(89)z - ``` - - ...Still no match for `pattern:$`. - - The search engine backtracks again. Backtracking generally works like this: the last greedy quantifier decreases the number of repetitions until it can. Then the previous greedy quantifier decreases, and so on. In our case the last greedy quantifier is the second `pattern:\d+`, from `subject:89` to `subject:8`, and then the star takes `subject:9`: - - ``` - X - \d+......\d+\d+ - (1234567)(8)(9)z - ``` -6. ...Fail again. The second and third `pattern:\d+` backtracked to the end, so the first quantifier shortens the match to `subject:123456`, and the star takes the rest: - - ``` - X - \d+.......\d+ - (123456)(789)z - ``` - - Again no match. The process repeats: the last greedy quantifier releases one character (`9`): - - ``` - X - \d+.....\d+ \d+ - (123456)(78)(9)z - ``` -7. ...And so on. - -The regular expression engine goes through all combinations of `123456789` and their subsequences. There are a lot of them, that's why it takes so long. - -A smart guy can say here: "Backtracking? Let's turn on the lazy mode -- and no more backtracking!". - -Let's replace `pattern:\d+` with `pattern:\d+?` and see if it works (careful, can hang the browser) - -```js run -// sloooooowwwwww -alert( '12345678901234567890123456789123456789z'.match(/(\d+?)*$/) ); -``` - -No, it doesn't. - -Lazy quantifiers actually do the same, but in the reverse order. Just think about how the search engine would work in this case. - -Some regular expression engines have tricky built-in checks to detect infinite backtracking or other means to work around them, but there's no universal solution. - -In the example above, when we search `pattern:<(\s*\w+=\w+\s*)*>` in the string `subject:<a=b a=b a=b a=b` -- the similar thing happens. - -The string has no `>` at the end, so the match is impossible, but the regexp engine does not know about it. The search backtracks trying different combinations of `pattern:(\s*\w+=\w+\s*)`: - -``` -(a=b a=b a=b) (a=b) -(a=b a=b) (a=b a=b) -... -``` - -## How to fix? - -The problem -- too many variants in backtracking even if we don't need them. - -For instance, in the pattern `pattern:(\d+)*$` we (people) can easily see that `pattern:(\d+)` does not need to backtrack. - -Decreasing the count of `pattern:\d+` can not help to find a match, there's no matter between these two: - -``` -\d+........ -(123456789)z - -\d+...\d+.... -(1234)(56789)z -``` - -Let's get back to more real-life example: `pattern:<(\s*\w+=\w+\s*)*>`. We want it to find pairs `name=value` (as many as it can). There's no need in backtracking here. - -In other words, if it found many `name=value` pairs and then can't find `>`, then there's no need to decrease the count of repetitions. Even if we match one pair less, it won't give us the closing `>`: - -Modern regexp engines support so-called "possessive" quantifiers for that. They are like greedy, but don't backtrack at all. Pretty simple, they capture whatever they can, and the search continues. There's also another tool called "atomic groups" that forbid backtracking inside parentheses. - -Unfortunately, but both these features are not supported by JavaScript. - -Although we can get a similar affect using lookahead. There's more about the relation between possessive quantifiers and lookahead in articles [Regex: Emulate Atomic Grouping (and Possessive Quantifiers) with LookAhead](http://instanceof.me/post/52245507631/regex-emulate-atomic-grouping-with-lookahead) and [Mimicking Atomic Groups](http://blog.stevenlevithan.com/archives/mimic-atomic-groups). - -The pattern to take as much repetitions as possible without backtracking is: `pattern:(?=(a+))\1`. - -In other words, the lookahead `pattern:?=` looks for the maximal count `pattern:a+` from the current position. And then they are "consumed into the result" by the backreference `pattern:\1`. - -There will be no backtracking, because lookahead does not backtrack. If it found like 5 times of `pattern:a+` and the further match failed, then it doesn't go back to 4. - -Let's fix the regexp for a tag with attributes from the beginning of the chapter`pattern:<\w+(\s*\w+=(\w+|"[^"]*")\s*)*>`. We'll use lookahead to prevent backtracking of `name=value` pairs: - -```js run -// regexp to search name=value -let attrReg = /(\s*\w+=(\w+|"[^"]*")\s*)/ - -// use it inside the regexp for tag -let reg = new RegExp('<\\w+(?=(' + attrReg.source + '*))\\1>', 'g'); - -let good = '...<a test="<>" href="#">... <b>...'; - -let bad = `<tag a=b a=b a=b a=b a=b a=b a=b a=b - a=b a=b a=b a=b a=b a=b a=b a=b a=b a=b a=b a=b a=b`; - -alert( good.match(reg) ); // <a test="<>" href="#">, <b> -alert( bad.match(reg) ); // null (no results, fast!) -``` - -Great, it works! We found a long tag `match:<a test="<>" href="#">` and a small one `match:<b>` and didn't hang the engine. - -Please note the `attrReg.source` property. `RegExp` objects provide access to their source string in it. That's convenient when we want to insert one regexp into another. diff --git a/6-async/01-callbacks/article.md b/6-async/01-callbacks/article.md deleted file mode 100644 index 0f81a2f891..0000000000 --- a/6-async/01-callbacks/article.md +++ /dev/null @@ -1,271 +0,0 @@ - - -# 導入: callbacks - -JavaScript の多くのアクションは *非同期* です。 - -例えば、次の `loadScript(src)` を見てください: - -```js -function loadScript(src) { - let script = document.createElement('script'); - script.src = src; - document.head.append(script); -} -``` - -この関数の目的は新しいスクリプトを読み込むことです。ドキュメントに `<script src="…">` を追加したとき、ブラウザはそれを読み込み、実行します。 - -このように使うことができます: - -```js -// スクリプトを読み込み、実行する -loadScript('/my/script.js'); -``` - -そのアクション(スクリプトの読み込み)は、今ではなく後で終わるため、関数は "非同期" と呼ばれます。 - -`loadScript` の呼び出しにより、スクリプトの読み込みを開始し、その後実行を続けます。スクリプトが読み込まれている間、それ以降のコードは実行が終わり、もし読み込みに時間がかかる場合は、他のスクリプトも実行される可能性があります。 - -```js -loadScript('/my/script.js'); -// loadScript の下のコードはスクリプトの読み込みが終わるのを待ちません -// ... -``` - -今、新しいスクリプトがロードされたときにそれを使いたいとします。恐らく新しい関数を宣言しているので、それらを実行したいとします。 - -...しかし、`loadScript(…)` 呼び出しの直後にそれをしても上手く動作しません: - -```js -loadScript('/my/script.js'); // このスクリプトは "function newFunction() {…}" を持っています - -*!* -newFunction(); // そのような関数はありません! -*/!* -``` - -もちろん、恐らくブラウザにはスクリプトを読み込む時間がありませんでした。そのため、新しい関数の即時呼び出しは失敗します。今のところ、`loadScript` 関数は読み込みの完了を追跡する方法を提供していません。スクリプトは読み込まれ、最終的に実行されますが、そのスクリプトの新しい関数や変数を使用するために、それらがいつ起きるのかを知りたいです。 - -`loadScript` に2つ目の引数に、スクリプトが読み込まれたときに実行する `callback` 関数を追加しましょう: - -```js -function loadScript(src, *!*callback*/!*) { - let script = document.createElement('script'); - script.src = src; - -*!* - script.onload = () => callback(script); -*/!* - - document.head.append(script); -} -``` - -ロードしたスクリプトにある新しい関数を呼びたい場合は callback に書きます。: - -```js -loadScript('/my/script.js', function() { - // コールバックはスクリプトがロード後に実行されます - newFunction(); // なので、これは動作します - ... -}); -``` - -That's the idea: 第2引数は、アクションが完了したときに実行される関数(通常は無名)です。 - -ここで、実際のスクリプトを使った実行可能な例を示します: - -```js run -function loadScript(src, callback) { - let script = document.createElement('script'); - script.src = src; - script.onload = () => callback(script); - document.head.append(script); -} - -*!* -loadScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js', script => { - alert(`Cool, the ${script.src} is loaded`); - alert( _ ); // ロードされたスクリプトで宣言されている関数 -}); -*/!* -``` - -これは "コールバックベース" と呼ばれる非同期プログラミングのスタイルです。非同期の処理をする関数は、関数が完了した後に実行するための `callback` を提供します。 - -ここでは `loadScript` でそれを行いましたが、もちろん一般的なアプローチです。 - -## Callback の中の callback - -2つのスクリプトを順次読み込む方法: 最初の1つを読み込み、2つ目はその後に読み込む? - -自然な解決策は、2つ目の `loadScript` 呼び出しを callback の中に置くことです。次のようになります: - -```js -loadScript('/my/script.js', function(script) { - - alert(`Cool, the ${script.src} is loaded, let's load one more`); - -*!* - loadScript('/my/script2.js', function(script) { - alert(`Cool, the second script is loaded`); - }); -*/!* - -}); -``` - -外側の `loadScript` の完了後、そのコールバックは内側の `loadScript` を開始します。 - -...仮にもっとスクリプトを読み込みたい場合はどうなりますか? - -```js -loadScript('/my/script.js', function(script) { - - loadScript('/my/script2.js', function(script) { - -*!* - loadScript('/my/script3.js', function(script) { - // ...すべてのスクリプトが読み込まれるまで続きます - }); -*/!* - - }) - -}); -``` - -したがって、すべての新しいアクションはコールバックの中です。少ないアクションの場合は問題ありませんが、多い場合には問題です。そのため、この後別の方法を見ていきます。 - -## エラー処理 - -上の例ではエラーを考慮していませんでした。もしスクリプト読み込みが失敗した場合どうしますか?コールバックはそれに対応する必要があります。 - -これは、読み込みエラーを追跡する `loadScript` の改良版です: - -```js run -function loadScript(src, callback) { - let script = document.createElement('script'); - script.src = src; - -*!* - script.onload = () => callback(null, script); - script.onerror = () => callback(new Error(`Script load error for ${src}`)); -*/!* - - document.head.append(script); -} -``` - -これは成功時に `callback(null, script)` を呼び、それ以外の場合には `callback(error)` を呼びます。 - -使用方法: -```js -loadScript('/my/script.js', function(error, script) { - if (error) { - // エラー処理 - } else { - // スクリプトの読み込みが成功 - } -}); -``` - -もう一度言いますが、`loadScript` で使った方法は、実際に非常に一般的なものです。これは "エラーファーストなコールバック" スタイルと呼ばれます。 - -慣例は次の通りです: -1. `callback` の最初の引数は、エラーが発生した場合のために予約されています。そして `callback(err)` が呼ばれます。 -2. 2つ目の引数(と必要に応じて以降の引数)は正常な結果を得るためのもので、`callback(null, result1, result2…)` が呼ばれます。 - -なので、単一の `callback` 関数は、エラー報告と結果渡し両方のために使われます。 - -## 破滅のピラミッド - -初見では、これは非同期コーディングの実行可能な方法です。確かにその通りです。1つまたは2つ程度のネストされた呼び出しの場合には問題なく見えます。 - -しかし、次々に続く複数の非同期アクションの場合、次のようなコードを持つことになります: - -```js -loadScript('1.js', function(error, script) { - - if (error) { - handleError(error); - } else { - // ... - loadScript('2.js', function(error, script) { - if (error) { - handleError(error); - } else { - // ... - loadScript('3.js', function(error, script) { - if (error) { - handleError(error); - } else { - *!* - // ...すべてのスクリプトが読み込まれるまで続く (*) - */!* - } - }); - - } - }) - } -}); -``` - -上記のコードでは: -1. `1.js` をロードし、エラーがなければ -2. `2.js` をロードします。エラーがなければ -3. `3.js` をロードします。エラーがなければ -- 他の何か `(*)` を行います。 - -呼び出しがよりネストされるにつれて、特に `...` ではなく、実際により多くのループや条件式などを含むコードがある場合、コードはより深くなり、益々管理が難しくなります。 - -これは "コールバック地獄" や "破滅のピラミッド" と呼ばれる場合があります。 - -![](callback-hell.png) - -ネストされた呼び出しの "ピラミッド" はすべての非同期アクションで右に成長していきます。まもなく、それは制御不能になります。 - -したがって、このコーディング方法はあまり良くありません。 - -私たちは、次のようにすべてのアクションをスタンドアロンの関数にすることで、この問題を軽減することができます。: - -```js -loadScript('1.js', step1); - -function step1(error, script) { - if (error) { - handleError(error); - } else { - // ... - loadScript('2.js', step2); - } -} - -function step2(error, script) { - if (error) { - handleError(error); - } else { - // ... - loadScript('3.js', step3); - } -} - -function step3(error, script) { - if (error) { - handleError(error); - } else { - // ...すべてのスクリプトが読み込まれた後に続く (*) - } -}; -``` - -どうでしょう? 同じことをしますが、今や深いネストはありません。なぜならすべてのアクションをトップレベルの関数に分離したからです。 - -これは機能しますが、コードはばらばらになったスプレッドシートのように見えます。これは読みにくく、あなたも気づいたでしょう。1つは、読んでいる間に関数間で視点のジャンプが必要になります。これは不便で、読者がコードに精通しておらず、どこに視点を移動させたらよいか分からない場合は特に困ります。 - -また、`step*` という名前の関数はすべて1度の使用であり、"破滅のピラミッド" を避けるためだけに作られています。だれもこのアクションの外でそれらを再利用するつもりはありません。そのため、ここには少し散らかっている名前空間があります。 - -私たちは何かより良いものを望んでいます。 - -幸いにも、このようなピラミッドを回避するための他のう方法があります。ベストな方法の1つは次のチャプターで説明する "promises" を使うことです。 diff --git a/6-async/01-callbacks/callback-hell.png b/6-async/01-callbacks/callback-hell.png deleted file mode 100644 index f42224a268..0000000000 Binary files a/6-async/01-callbacks/callback-hell.png and /dev/null differ diff --git a/6-async/01-callbacks/callback-hell@2x.png b/6-async/01-callbacks/callback-hell@2x.png deleted file mode 100644 index 7c9a3ab5ed..0000000000 Binary files a/6-async/01-callbacks/callback-hell@2x.png and /dev/null differ diff --git a/6-async/02-promise-basics/article.md b/6-async/02-promise-basics/article.md deleted file mode 100644 index 9b26f47c88..0000000000 --- a/6-async/02-promise-basics/article.md +++ /dev/null @@ -1,289 +0,0 @@ -# Promise - -あなたはトップシンガーで、ファンは今後のシングルについて絶えず尋ねていると想像してください。 - -それに関していくらか解放されるため、あなたは公開時にその曲を送ることを約束します。 また、ファンに更新を購読できるリストを提供します。 彼らは電子メールアドレスを記入することができ、曲が利用可能になるとすべての購読者がすぐにそれを受け取れるようになります。 そして、万が一何か手違いがありその曲を発表する計画が取り消されたとしても、彼らはその通知を受けとることができるでしょう。 - -みんな幸せです: ファンはこれ以上あなたの元へ押し寄せることはしません。また、ファンはその歌を見逃すことはありません。 - -これはプログラミングにおいてしばしば抱くことへの現実的なアナロジーです。: - -1. 何かを行い時間を必要とする "生成コード"。例えば、コードはリモートスクリプトをロードします。それは "シンガー" です。 -2. 準備が整ったらすぐ "生成コード" の結果が欲しい "消費コード"。多くの関数がその結果を必要とするかもしれません。それらは "ファン" です。 -3. *promise* は "生成コード" と "消費コード" をリンクする特別な JavaScript オブジェクトです。今回のアナロジーではそれは "購読リスト" です。生成コードは約束された結果を生成するために必要な時間をとります。そして "promise" は準備ができたら、購読したすべてのコードが結果を利用できるようにします。 - -JavaScript の promise は追加の特徴や制限があり単純な 購読リスト よりも複雑であるため、このアナロジーはあまり正確ではありません。しかし、最初に理解には良いです。 - -promise オブジェクトのコンストラクタ構文は次の通りです: - -```js -let promise = new Promise(function(resolve, reject) { - // executor (生成コード, "シンガー") -}); -``` - -`new Promise` へ渡される関数は *executor(執行者)* と呼ばれます。primise が作成されると、自動的に呼ばれます。それは最終的に結果と一緒に終了する生成コードを含んでいます。上記のアナロジーの言葉では、executor は "シンガー" です。 - -生成された `promise` オブジェクトは内部プロパティを持っています: - -- `state` -- 最初は "pending(保留中)" であり、その後 "fultilled(完了)" もしくは "rejected(拒否)" に変更されます。 -- `result` -- 任意の値です。初期値は `undefined` です。 - -executor がジョブを終了した時、次の中のいずれか1つを呼びます: - -- `resolve(value)` -- ジョブが正常に終了したことを示します。: - - `state` を `"fulfilled"` に設定します, - - `result` を `value` に設定します. -- `reject(error)` -- エラーが発生したことを示します: - - `state` を `"rejected"` に設定します, - - `result` を `error` に設定します. - -![](promise-resolve-reject.png) - -ここには、シンプルな executor があります。: - -```js run -let promise = new Promise(function(resolve, reject) { - // promise が作られたとき、関数は自動的に実行されます - - alert(resolve); // function () { [native code] } - alert(reject); // function () { [native code] } - - // 1秒後、ジョブが "done!" という結果と一緒に完了したことを合図します - setTimeout(() => *!*resolve("done!")*/!*, 1000); -}); -``` - -上のコードを実行すると2つの事が見えます: - -1. executor は自動的かつ即座に呼ばれます(`new Promise` によって)。 -2. executor は2つの引数を受け取ります: `resolve` と `reject` です -- これらの関数は JavaScript エンジンから来ており、これらを作る必要はありません。代わりに、executor は準備ができた際にそれらを呼ぶ必要があります。 - -1秒後、executor は結果を生成するために `resolve("done")` を呼び出します。: - -![](promise-resolve-1.png) - -これは、 "ジョブが正常に完了した" 例でした。 - -そして、次はエラーで executor が promise を拒否する例です。: - -```js -let promise = new Promise(function(resolve, reject) { - // 1秒後、ジョブがエラーで終わったことを合図します - setTimeout(() => *!*reject(new Error("Whoops!"))*/!*, 1000); -}); -``` - -![](promise-reject-1.png) - -要約すると、executor はジョブ(通常は時間のかかる何か)を行い、その後、対応する promise オブジェクトの状態を変更するために、`resolve` または `reject` を呼び出します。 - -解決または拒否されている promise は、 "pending" の promise とは対象的に "settled" と呼ばれます。 - -````smart header="1つの結果またはエラーのみです" -executor は1つの `resolve` または `reject` だけを呼びだす必要があります。promise の状態の変化は最後のものです。 - -さらなるすべての `resolve` や `reject` は無視されます: - -```js -let promise = new Promise(function(resolve, reject) { - resolve("done"); - - reject(new Error("…")); // 無視されます - setTimeout(() => resolve("…")); // 無視されます -}); -``` - -この考えは、executor により行われたジョブは1つの結果またはエラーのみを持つということです。プログラミングでは、ストリームやキューなど、多くの "フロー" の結果を許容する他のデータ構造が存在します。それらには promise と比較したときに長所と短所があります。それらは JavaScript のコアではサポートされておらず、promsise が提供する幾つかの言語機能が不足していますが、ここでは promise に集中するためにそれらは説明しません。 - -また、1つ以上の引数で `resolve/reject` を呼び出した場合、最初の引数が使われ、次の引数は無視されます。 -```` - -```smart header="`Error` オブジェクトで reject する" -技術的には、任意の型の引数で `reject` を呼び出すことが可能です(`resolve` のように)。しかし、`reject` (またはそれを継承したもの)では、`Error` オブジェクトを利用することを推奨します。その理由は後ほど明らかになります。 -``` - -````smart header="Resolve/reject can be immediate" -実際には、executor は通常非同期で何かを行い、暫く経過した後に `resolve/reject` を呼び出しますが、それは必須ではありません。次のように、すぐに `resolve` や `reject` を呼び出すことが可能です。: - -```js -let promise = new Promise(function(resolve, reject) { - resolve(123); // 即座に結果を返します: 123 -}); -``` - -例えば、ジョブの開始後、すでに完了していることが分かったとき等です。技術的には即座に promise を解決することは問題ありません。 -```` - -```smart header="`state` と `result` は内部のプロパティです" -promise オブジェクトのプロパティ `state` と `result` は内部的なものです。我々のコードから直接アクセスすることはできません。代わりに `.then/catch` メソッドを利用します。それらについては下で説明します。 -``` - -## 消費者: ".then" and ".catch" - -promise オブジェクトは生成コード(executor)と消費関数(結果/エラーを受け取りたいもの)の間のリンクとして機能します。消費関数は `promise.then` と `promise.catch` メソッドを使用して登録することができます。 - -`.then` の構文は次の通りです: - -```js -promise.then( - function(result) { /* 成功した結果を扱う */ }, - function(error) { /* エラーを扱う */ } -); -``` - -最初の関数の引数は、promise が解決され結果を得たときに実行されます。そして2つ目は -- 拒否され、エラーを取得したときに実行されます。 - -例: - -```js run -let promise = new Promise(function(resolve, reject) { - setTimeout(() => resolve("done!"), 1000); -}); - -// resolve は .then の最初の関数を実行する -promise.then( -*!* - result => alert(result), // 1秒後に "done!" を表示 -*/!* - error => alert(error) // 実行されない -); -``` - -拒否の場合: - -```js run -let promise = new Promise(function(resolve, reject) { - setTimeout(() => reject(new Error("Whoops!")), 1000); -}); - -// reject は .then の2つ目の関数を実行する -promise.then( - result => alert(result), // 実行されない -*!* - error => alert(error) // 1秒後に "Error: Whoops!" を表示 -*/!* -); -``` - -もし、正常完了の場合だけを扱いたい場合は、`.then` に1つの引数だけを指定することもできます。: - -```js run -let promise = new Promise(resolve => { - setTimeout(() => resolve("done!"), 1000); -}); - -*!* -promise.then(alert); // 1秒後に "done!" を表示 -*/!* -``` - -エラーの場合だけ興味があれば、`.then(null, function)` またはその "エイリアス" である `.catch(function)` を使います。 - - -```js run -let promise = new Promise((resolve, reject) => { - setTimeout(() => reject(new Error("Whoops!")), 1000); -}); - -*!* -// .catch(f) は promise.then(null, f) と同じです -promise.catch(alert); // 1秒後に "Error: Whoops!" を表示 -*/!* -``` - -`.catch(f)` の呼び出しは、`.then(null, f)` の完全な類似物であり、単に簡略化したものです。 - -````smart header="On settled promises `then` runs immediately" -promise が pending の場合、`.then/catch` ハンドラは結果を待ちます。そうではなく、promise がすでに settled である場合、直ちに実行されます。: - -```js run -// 即座に promise が解決されます -let promise = new Promise(resolve => resolve("done!")); - -promise.then(alert); // done! (すぐに表示されます) -``` - -それは、時間がかかることもあり、すぐに終わることもあるジョブにとっては便利です。ハンドラは両方の場合に実行されることが保証されています。 -```` - -````smart header="`.then/catch` のハンドラは常に非同期です" -さらに正確に言うと、`.then/catch` ハンドラが実行されるとき、それは最初に内部キューに入ります。JavaScript エンジンはキューからハンドラを取り出し、`setTimeout(..., 0)` と同じように現在のコードが終了した際に実行します。 - -言い換えると、`.then(handler)` がトリガするとき、それは `setTimeout(handler, 0)` のようなことをします。 - -下の例では、promise は直ちに解決されます。なので、`.then(alert)` はすぐにトリガします: `alert` 呼び出しはキューに格納され、コードが終了した後即座に実行します。 - -```js run -// 即座に解決された promise -let promise = new Promise(resolve => resolve("done!")); - -promise.then(alert); // done! (現在のコード終了直後) - -alert("code finished"); // このアラートが最初に表示されます -``` - -したがって、`.then` の後にあるコードは常にハンドラの前に実行されます(たとえ事前に解決された promise だとしても)。通常それは重要ではありませんが、場面によっては重要な場合があります。 -```` - -さて、非同期コードを書くにあたり、promise がどのように役立つか、より実践的な例を見てみましょう。 - -## 例: loadScript - -以前のチャプターで、スクリプトを読み込むための関数 `loadScript` がありました。 - -思い出すために、ここにコールバックベースのパターンを示します。: - -```js -function loadScript(src, callback) { - let script = document.createElement('script'); - script.src = src; - - script.onload = () => callback(null, script); - script.onerror = () => callback(new Error(`Script load error ` + src)); - - document.head.append(script); -} -``` - -promise を使って再実装してみましょう。 - -新しい関数 `loadScript` はコールバックを必要としません。代わりに、読み込みが完了したときに解決する promise オブジェクトを生成し返します。外部コードは `.then` を使用してそれにハンドラを追加することができます。: - -```js run -function loadScript(src) { - return new Promise(function(resolve, reject) { - let script = document.createElement('script'); - script.src = src; - - script.onload = () => resolve(script); - script.onerror = () => reject(new Error("Script load error: " + src)); - - document.head.append(script); - }); -} -``` - -使用方法: - -```js run -let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js"); - -promise.then( - script => alert(`${script.src} is loaded!`), - error => alert(`Error: ${error.message}`) -); - -promise.then(script => alert('One more handler to do something else!')); -``` - -コールバックベースの構文に比べて、すぐには利益はほとんどありません。: - -```compare minus="Callbacks" plus="Promises" -- `loadScript` を呼び出す際、`callback` 関数を準備する必要があります。つまり、`loadScript` が呼ばれる *前* に、その結果で何をするのか知っておかなければなりません。 -- コールバックは1つだけです。 -+ Promise を使うと自然な順番で物事をコード化することができます。最初に `loadScript` を走らせ、`.then` はその結果をどうするかを記述します。 -+ いつでも、必要なだけ promise に対する `.then` を呼び出すことが可能です。 -``` - -そのため、promise は既により良いコードフローと柔軟性をもたらします。しかしもっと多くのことがあります。それらについては次のチャプターで見ていきましょう。 diff --git a/6-async/02-promise-basics/promise-init.png b/6-async/02-promise-basics/promise-init.png deleted file mode 100644 index c54ce05f1d..0000000000 Binary files a/6-async/02-promise-basics/promise-init.png and /dev/null differ diff --git a/6-async/02-promise-basics/promise-init@2x.png b/6-async/02-promise-basics/promise-init@2x.png deleted file mode 100644 index 82f0d7138c..0000000000 Binary files a/6-async/02-promise-basics/promise-init@2x.png and /dev/null differ diff --git a/6-async/02-promise-basics/promise-reject-1.png b/6-async/02-promise-basics/promise-reject-1.png deleted file mode 100644 index 38f27c735a..0000000000 Binary files a/6-async/02-promise-basics/promise-reject-1.png and /dev/null differ diff --git a/6-async/02-promise-basics/promise-reject-1@2x.png b/6-async/02-promise-basics/promise-reject-1@2x.png deleted file mode 100644 index f9b748ff0a..0000000000 Binary files a/6-async/02-promise-basics/promise-reject-1@2x.png and /dev/null differ diff --git a/6-async/02-promise-basics/promise-resolve-1.png b/6-async/02-promise-basics/promise-resolve-1.png deleted file mode 100644 index 0dfe7bada2..0000000000 Binary files a/6-async/02-promise-basics/promise-resolve-1.png and /dev/null differ diff --git a/6-async/02-promise-basics/promise-resolve-1@2x.png b/6-async/02-promise-basics/promise-resolve-1@2x.png deleted file mode 100644 index 269b8f2908..0000000000 Binary files a/6-async/02-promise-basics/promise-resolve-1@2x.png and /dev/null differ diff --git a/6-async/02-promise-basics/promise-resolve-reject.png b/6-async/02-promise-basics/promise-resolve-reject.png deleted file mode 100644 index 505726efd5..0000000000 Binary files a/6-async/02-promise-basics/promise-resolve-reject.png and /dev/null differ diff --git a/6-async/02-promise-basics/promise-resolve-reject@2x.png b/6-async/02-promise-basics/promise-resolve-reject@2x.png deleted file mode 100644 index 31128dbe09..0000000000 Binary files a/6-async/02-promise-basics/promise-resolve-reject@2x.png and /dev/null differ diff --git a/6-async/03-promise-chaining/article.md b/6-async/03-promise-chaining/article.md deleted file mode 100644 index 8ea0ec8565..0000000000 --- a/6-async/03-promise-chaining/article.md +++ /dev/null @@ -1,685 +0,0 @@ - -# Promises チェーン - -チャプター <info:callbacks> で言及した問題に戻りましょう。 - -- 私たちは次々に実行される一連の非同期タスクを持っています。例えば、スクリプトの読み込みです。 -- 上手くコード化するにはどうすればよいでしょう? - -Promise はそれをするためのいくつかの方法を提供します。 - -[cut] - -このチャプターでは promise チェーンを説明します。 - -次のようになります: - -```js run -new Promise(function(resolve, reject) { - - setTimeout(() => resolve(1), 1000); // (*) - -}).then(function(result) { // (**) - - alert(result); // 1 - return result * 2; - -}).then(function(result) { // (***) - - alert(result); // 2 - return result * 2; - -}).then(function(result) { - - alert(result); // 4 - return result * 2; - -}); -``` - -この考え方は、結果が `.then` ハンドラのチェーンを通じて渡されるということです。 - -ここでの流れは次の通りです: -1. 最初の promise は1秒で解決されます `(*)`, -2. その後、`.then` ハンドラが呼ばれます `(**)`, -3. 返却された値は次の `.then` ハンドラへ渡されます `(***)`, -4. ...同様に続きます。 - -結果がハンドラのチェーンに沿って渡されるので、一連の `alert` 呼び出しは `1` -> `2` -> `4` の順番で表示されます。 - -![](promise-then-chain.png) - -`promise.then` の呼び出しは promise を返すので、続けて次の `.then` を呼び出すことができます。そのためすべてのコードが機能します。 - -ハンドラが値を返すとき、それは promise の結果になります。なので、次の `.then` はそれと一緒に呼ばれます。 - -これらの言葉をより明確にするために、ここではチェーンの始まりがあります: - -```js run -new Promise(function(resolve, reject) { - - setTimeout(() => resolve(1), 1000); - -}).then(function(result) { - - alert(result); - return result * 2; // <-- (1) - -}) // <-- (2) -// .then… -``` - -`.then` により返却される値は promise であるため、`(2)` で別の `.then` を追加することができます。`(1)` で値が返却されるとき、その promise は解決されるため、次のハンドラはその値で実行されます。 - -チェーンとは異なり、技術的には次のように1つの promise へ多くの `.then` を追加することも可能です。: - -```js run -let promise = new Promise(function(resolve, reject) { - setTimeout(() => resolve(1), 1000); -}); - -promise.then(function(result) { - alert(result); // 1 - return result * 2; -}); - -promise.then(function(result) { - alert(result); // 1 - return result * 2; -}); - -promise.then(function(result) { - alert(result); // 1 - return result * 2; -}); -``` - -...しかし、これは完全に別物です。ここに図があります(上記のチェーンと比較してください): - -![](promise-then-many.png) - -同一の promise 上のすべての `.then` は同じ結果を得ます -- その promise の結果です。従って、上のコードでは、すべての `alert` は同じ `1` を表示します。それらの間での結果渡しはありません。 - -実際には、単一の promise に対し複数のハンドラが必要なケースはほとんどありません。チェーンの方がはるかに多く利用されます。 - -## promise の返却  - -通常、`.then` ハンドラにより返却された値は、直ちに次のハンドラに渡されます。しかし例外もあります。 - -もし返却された値が promise である場合、それ以降の実行はその promise が解決するまで中断されます。その後、promise の結果が次の `.then` ハンドラに渡されます。 - -例: - -```js run -new Promise(function(resolve, reject) { - - setTimeout(() => resolve(1), 1000); - -}).then(function(result) { - - alert(result); // 1 - -*!* - return new Promise((resolve, reject) => { // (*) - setTimeout(() => resolve(result * 2), 1000); - }); -*/!* - -}).then(function(result) { // (**) - - alert(result); // 2 - - return new Promise((resolve, reject) => { - setTimeout(() => resolve(result * 2), 1000); - }); - -}).then(function(result) { - - alert(result); // 4 - -}); -``` - -ここで最初の `.then` は `1` を表示し、行 `(*)` で `new Promise(…)` を返します。1秒後、それは解決され、結果(`resolve` の引数, ここでは `result*2`) は行 `(**)` にある2番目の `.then` のハンドラに渡されます。それは `2` を表示し、同じことをします。 - -したがって、出力は再び 1 -> 2 > 4 ですが、今は `alert` 呼び出しの間に 1秒の遅延があります。 - -promise を返却することで、非同期アクションのチェーンを組み立てることができます。 - -## 例: loadScript - -`loadScript` でこの機能を使って、スクリプトを1つずつ順番にロードしてみましょう。: - -```js run -loadScript("/article/promise-chaining/one.js") - .then(function(script) { - return loadScript("/article/promise-chaining/two.js"); - }) - .then(function(script) { - return loadScript("/article/promise-chaining/three.js"); - }) - .then(function(script) { - // それらがロードされていることを表示するために、スクリプトで宣言されている関数を使用 - one(); - two(); - three(); - }); -``` - -ここで、各 `loadScript` 呼び出しは promise を返し、次の `.then` はそれが解決されたときに実行されます。その後、次のスクリプトのロードを開始します。そのため、スクリプトは次々にロードされます。 - -私たちは、このチェーンにより多くの非同期アクションを追加することができます。ここで、このコードは依然として "フラット" であることに注目してください。それは大きくなっていますが右にではありません。"破滅のピラミッド" の兆候はありません。 - -技術的にはそれぞれの promise の後に、次のように promise を返却することなく直接 `.then` を書くことも可能であることに留意してください。: - -```js run -loadScript("/article/promise-chaining/one.js").then(function(script1) { - loadScript("/article/promise-chaining/two.js").then(function(script2) { - loadScript("/article/promise-chaining/three.js").then(function(script3) { - // この関数は変数 script1, script2 と script3 へアクセスすることができます - one(); - two(); - three(); - }); - }); -}); -``` - -このコードは同じことをします: 順番に3つのスクリプトをロードします。しかし、"右に大きくなります"。そのため、コールバックと同じ問題があります。それを避けるためにチェーン(`.then` から promise を返す)を使用してください。 - -ネストされた関数が外側のスコープ(ここでは最もネストしているコールバックはすべての変数 `scriptX` へアクセスできます)にアクセスできるため、 `.then` を直接書くこともできますが、それはルールではなく例外です。 - - -````smart header="Thenables" -正確には、`.then` は任意の "thenable" オブジェクトを返す可能性があり、それは promise として同じように扱われます。 - -"thenable" オブジェクトとは、メソッド `.then` を持つオブジェクトです。 - -この思想は、サードパーティライブラリが彼ら自身の "promise 互換な" オブジェクトを実装できるというものです。それらは拡張されたメソッドのセットを持つことができますが、`.then` を実装しているため、ネイティブの promise とも互換があります。 - -これは thenable オブジェクトの例です: - -```js run -class Thenable { - constructor(num) { - this.num = num; - } - then(resolve, reject) { - alert(resolve); // function() { native code } - // 1秒後に this.num*2 で resolve する - setTimeout(() => resolve(this.num * 2), 1000); // (**) - } -} - -new Promise(resolve => resolve(1)) - .then(result => { - return new Thenable(result); // (*) - }) - .then(alert); // 1000ms 後に 2 を表示 -``` - -JavaScript は行 `(*)` で `.then` ハンドラによって返却されたオブジェクトをチェックします: もし `then` という名前のメソッドが呼び出し可能であれば、ネイティブ関数 `resolve`, `reject` を引数として(executor 似ています)それを呼び出し、それらのいずれかが呼び出されるまで待ちます。上の例では、`resolve(2)` が1秒後に `(**)` で呼ばれます。その後、結果はチェーンのさらに下に渡されます。 - -この特徴により、カスタムオブジェクトを `Promise` から継承することなく、promise チェーンで統合することができます。 -```` - - -## より大きな例: fetch - -フロントエンドのプログラミングでは、promise はネットワークリクエストの場合にしばしば使われます。なので、その拡張された例を見てみましょう。 - -私たちは、リモートサーバからユーザに関する情報をロードするために [fetch](mdn:api/WindowOrWorkerGlobalScope/fetch) メソッドを使います。メソッドは非常に複雑で、多くの任意パラメータがありますが、基本の使い方はとてもシンプルです: - -```js -let promise = fetch(url); -``` - -これは、`url` へネットワークリクエストを行い、promise を返します。promise はリモートサーバがヘッダーで応答するとき、*完全なレスポンスがダウンロードされる前に* `response` オブジェクトで解決されます。 - -完全なレスポンスを見るためには、`response.text()` メソッドを呼ぶ必要があります: これは完全なテキストがリモートサーバからダウンロードされたときに解決され、そのテキストを結果とする promise を返します。 - -以下のコードは `user.json` へリクエストを行い、サーバからそのテキストをロードします: - -```js run -fetch('/article/promise-chaining/user.json') - // この .then はリモートサーバが応答したときに実行されます - .then(function(response) { - // response.text() は、レスポンスのダウンロードが完了した際に - // 完全なレスポンステキストで解決される新たな promise を返します - return response.text(); - }) - .then(function(text) { - // ...そして、ここではリモートファイルの中身が参照できます - alert(text); // {"name": "iliakan", isAdmin: true} - }); -``` - -リモートデータを読んで、JSON としてパースするメソッド `response.json()` もあります。我々のケースでは、より一層便利なのでそれに置き換えてみます。 - -わかりやすくするために、アロー関数も使います: - -```js run -// 上と同じですが、response.json() はリモートコンテンツを JSON としてパースします -fetch('/article/promise-chaining/user.json') - .then(response => response.json()) - .then(user => alert(user.name)); // iliakan -``` - -次に、ロードしたユーザで何かしてみましょう。 - -例えば、github へもう1つリクエストを行い、ユーザプロフィールを読み込みアバターを表示させてみます。: - -```js run -// user.json へのリクエスト -fetch('/article/promise-chaining/user.json') - // json としてロード - .then(response => response.json()) - // github へのリクエスト - .then(user => fetch(`https://api.github.com/users/${user.name}`)) - // json としてロード - .then(response => response.json()) - // 3秒間アバター画像を表示 (githubUser.avatar_url) - .then(githubUser => { - let img = document.createElement('img'); - img.src = githubUser.avatar_url; - img.className = "promise-avatar-example"; - document.body.append(img); - - setTimeout(() => img.remove(), 3000); // (*) - }); -``` - -このコードは動作します(コードの詳細についてはコメントをみてください)が、完全に自己記述的であるべきです。ここには promise を使い始める人が行う典型的な問題があります。 - -行 `(*)` を見てください: アバターの表示が終了して削除された *後* に何かをするにはどうすればいいでしょうか?例えば、ユーザ情報を編集するためのフォームを表示したいとします。今のところ、方法はありません。 - -チェーンを拡張可能にするには、アバターの表示が終了したときに resolve を行う promise を返す必要があります。 - -次のようになります: - -```js run -fetch('/article/promise-chaining/user.json') - .then(response => response.json()) - .then(user => fetch(`https://api.github.com/users/${user.name}`)) - .then(response => response.json()) -*!* - .then(githubUser => new Promise(function(resolve, reject) { -*/!* - let img = document.createElement('img'); - img.src = githubUser.avatar_url; - img.className = "promise-avatar-example"; - document.body.append(img); - - setTimeout(() => { - img.remove(); -*!* - resolve(githubUser); -*/!* - }, 3000); - })) - // 3秒後にトリガされます - .then(githubUser => alert(`Finished showing ${githubUser.name}`)); -``` - -今、`setTimeout` は `img.resolve()` を実行した直後に `resolve(githubUser)` を呼び出します。なので、チェーン内の次の `.then` に制御を渡し、ユーザデータを転送します。 - -ルールとして、非同期アクションは常に promise を返すべきです。 - -これは、あるアクションの後に別のアクションを実行させることができます。たとえ現時点ではチェーンの拡張予定はなくても、後で必要になるかもしれません。 - -最後に、先程のコードは再利用可能な関数に分割できます: - -```js run -function loadJson(url) { - return fetch(url) - .then(response => response.json()); -} - -function loadGithubUser(name) { - return fetch(`https://api.github.com/users/${name}`) - .then(response => response.json()); -} - -function showAvatar(githubUser) { - return new Promise(function(resolve, reject) { - let img = document.createElement('img'); - img.src = githubUser.avatar_url; - img.className = "promise-avatar-example"; - document.body.append(img); - - setTimeout(() => { - img.remove(); - resolve(githubUser); - }, 3000); - }); -} - -// 上記を使う: -loadJson('/article/promise-chaining/user.json') - .then(user => loadGithubUser(user.name)) - .then(showAvatar) - .then(githubUser => alert(`Finished showing ${githubUser.name}`)); - // ... -``` - -## エラーハンドリング - -非同期アクションは失敗する可能性があります: エラーの場合、対応する promise は reject されます。例えば、リモートサーバが利用不可で `fetch` が失敗する場合です。エラー(拒否/reject)を扱うには `.catch` を使います。 - -promise のチェーンはその点で優れています。promise が reject されると、コントロールはチェーンに沿って最も近い reject ハンドラにジャンプします。それは実際に非常に便利です。 - -例えば、下のコードでは URL が誤っており(存在しないサーバ)、`.catch` がエラーをハンドリングします: - -```js run -*!* -fetch('https://no-such-server.blabla') // rejects -*/!* - .then(response => response.json()) - .catch(err => alert(err)) // TypeError: failed to fetch (エラーメッセージ内容は異なる場合があります) -``` - -または、おそらくサーバはすべて正しく動作したが、応答が有効な JSON でない場合: - -```js run -fetch('/') // fetch はうまく動作し、サーバは成功を応答します -*!* - .then(response => response.json()) // rejects: ページが HTML で有効な json ではなかった場合 -*/!* - .catch(err => alert(err)) // SyntaxError: Unexpected token < in JSON at position 0 -``` - -下の例では、アバターの読み込みと表示のチェーンでのすべてのエラーを処理する `.catch` を追加しています: - -```js run -fetch('/article/promise-chaining/user.json') - .then(response => response.json()) - .then(user => fetch(`https://api.github.com/users/${user.name}`)) - .then(response => response.json()) - .then(githubUser => new Promise(function(resolve, reject) { - let img = document.createElement('img'); - img.src = githubUser.avatar_url; - img.className = "promise-avatar-example"; - document.body.append(img); - - setTimeout(() => { - img.remove(); - resolve(githubUser); - }, 3000); - })) - .catch(error => alert(error.message)); -``` - -ここでは、`.catch` はまったく呼ばれません。なぜならエラーが起きていないからです。しかし、上の prmise のいずれかが reject となった場合、catch は実行されます。 - -## 暗黙の try..catch - -executor と promise ハンドラのコードは "見えない `try..catch`" を持っています。エラーが起きた場合、キャッチして reject として扱います。 - -例えば、このコードを見てください: - -```js run -new Promise(function(resolve, reject) { -*!* - throw new Error("Whoops!"); -*/!* -}).catch(alert); // Error: Whoops! -``` - -...これは次のと同じように動作します: - -```js run -new Promise(function(resolve, reject) { -*!* - reject(new Error("Whoops!")); -*/!* -}).catch(alert); // Error: Whoops! -``` - -executor にある "見えない `try..catch` はエラーを自動的にキャッチし reject として扱っています。 - -これは executor だけでなくハンドラの中でも同様です。`.then` ハンドラの中で `throw` した場合、promise の reject を意味するので、コントロールは最も近いエラーハンドラにジャンプします。 - -ここにその例があります: - -```js run -new Promise(function(resolve, reject) { - resolve("ok"); -}).then(function(result) { -*!* - throw new Error("Whoops!"); // promise を rejects -*/!* -}).catch(alert); // Error: Whoops! -``` - -また、これは `throw` だけでなく同様にプログラムエラーを含む任意のエラーに対してです: - -```js run -new Promise(function(resolve, reject) { - resolve("ok"); -}).then(function(result) { -*!* - blabla(); // このような関数はありません -*/!* -}).catch(alert); // ReferenceError: blabla is not defined -``` - -副作用として、最後の `.catch` は明示的な reject だけでなく、上記のハンドラのような偶発的なエラーもキャッチします。 - -## 再スロー - -すでにお気づきのように、`.catch` は `try..catch` のように振る舞います。私たちは必要な数の `.then` を持ち、最後に単一の `.catch` を使用してすべてのエラーを処理します。 - -通常の `try..catch` では、エラーを解析し、処理できない場合は再スローする場合があります。promise でも同じことが可能です。`.catch` の中で `throw` する場合、コントロールは次の最も近いエラーハンドラにジャンプします。そして、エラーを処理して正常に終了すると、次に最も近い成功した `.then` ハンドラに続きます。 - -下の例では、`.catch` がエラーを正常に処理しています: - -```js run -// 実行: catch -> then -new Promise(function(resolve, reject) { - - throw new Error("Whoops!"); - -}).catch(function(error) { - - alert("The error is handled, continue normally"); - -}).then(() => alert("Next successful handler runs")); -``` - -ここでは、`.catch` ブロックが正常に終了しています。なので、次の成功ハンドラが呼ばれます。また何かを返すこともでき、その場合も同じです(値が渡されます)。 - -...そしてここでは `.catch` ブロックはエラーを解析し、再度スローしています: - -```js run -// 実行: catch -> catch -> then -new Promise(function(resolve, reject) { - - throw new Error("Whoops!"); - -}).catch(function(error) { // (*) - - if (error instanceof URIError) { - // エラー処理 - } else { - alert("Can't handle such error"); - -*!* - throw error; // ここで投げられたエラーは次の catch へジャンプします -*/!* - } - -}).then(function() { - /* 実行されません */ -}).catch(error => { // (**) - - alert(`The unknown error has occurred: ${error}`); - // 何も返しません => 実行は通常通りに進みます - -}); -``` - -ハンドラ `(*)` はエラーをキャッチしましたが処理していません。なぜなら、`URIError` ではないからです。なので、再びスローします。その後、実行は次の `.catch` へ移ります。 - -下のセクションでは、再スローの実際的な例を見ていきます。 - -## Fetch エラー処理の例 - -ユーザ読み込みの例のエラー処理を改善しましょう。 - -[fetch](mdn:api/WindowOrWorkerGlobalScope/fetch) により返却された promise は、要求を行うことができない場合に reject します。例えば、リモートサーバが利用不可の場合、または URL が不正な場合です。しかし、リモートサーバが 404 や 500 エラーといったの応答の場合、有効な応答とみなします。 - -仮にサーバが行 `(*)` で 500 エラーの非JSONページを返したらどうなるでしょう?そのようなユーザはおらず、github が `(**)` で 404 エラーを返すとどうなるでしょう? - -```js run -fetch('no-such-user.json') // (*) - .then(response => response.json()) - .then(user => fetch(`https://api.github.com/users/${user.name}`)) // (**) - .then(response => response.json()) - .catch(alert); // SyntaxError: Unexpected token < in JSON at position 0 - // ... -``` - -現時点では、コードは何が起きても応答を JSON として読み込もうとし、構文エラーで死にます。`no-such-user.json` は存在しないので、上の例を実行することでそれを見ることができます。 - -このエラーはチェーンを通じて失敗したものであり、詳細(何が失敗したのか、どこで失敗したのか)を含まないため良くありません。 - -したがって、もう1ステップ追加しましょう: HTTP ステータスが持っている `response.status` をチェックします。それが 200 でなければエラーをスローします。 - -```js run -class HttpError extends Error { // (1) - constructor(response) { - super(`${response.status} for ${response.url}`); - this.name = 'HttpError'; - this.response = response; - } -} - -function loadJson(url) { // (2) - return fetch(url) - .then(response => { - if (response.status == 200) { - return response.json(); - } else { - throw new HttpError(response); - } - }) -} - -loadJson('no-such-user.json') // (3) - .catch(alert); // HttpError: 404 for .../no-such-user.json -``` - -1. 他のエラータイプと区別するために HTTP エラーのためのカスタムクラスを作ります。さらに、新しいクラスは `response` オブジェクトを受け取り、エラーに保存するコンストラクタを持ちます。これにより、エラー処理のコードがアクセスできるようになります。 -2. 次に、リクエストとエラー処理のコードを `url` へ fetch する関数に置き、 *加えて* 任意の非 200 ステータスをエラーとして扱います。 -3. これで `alert` はより良いメッセージが表示されます。 - -エラーに対する独自のクラスを持つことの素晴らしい点は、エラー処理コードで簡単にチェックできることです。 - -例えば、リクエストを行い 404 となった場合 -- ユーザに情報を変更するよう依頼します。 - - -下のコードは github から指定された名前のユーザを読み込みます。もし存在しないユーザであれば、正しい名前を訪ねます。: - -```js run -function demoGithubUser() { - let name = prompt("Enter a name?", "iliakan"); - - return loadJson(`https://api.github.com/users/${name}`) - .then(user => { - alert(`Full name: ${user.name}.`); // (1) - return user; - }) - .catch(err => { -*!* - if (err instanceof HttpError && err.response.status == 404) { // (2) -*/!* - alert("No such user, please reenter."); - return demoGithubUser(); - } else { - throw err; - } - }); -} - -demoGithubUser(); -``` - -ここでは: - -1. `loadJson` が有効なユーザオブジェクトを返した場合、名前は `(1)` で表示されユーザが返されます。これによりユーザ関連のアクションをチェーンに追加できます。その場合、以下の `.catch`は無視され、すべてが非常にシンプルで問題ありません。 -2. それ以外の場合は、エラーの場合は行 `(2)` でチェックします。確かに HTTP エラーでステータスが 404(見つからない) の場合、ユーザに再入力を依頼します。他のエラーの場合は、処理の仕方を知らないため再スローします。 - -## 未処理の reject - -エラーが処理されない場合何がおきるでしょう?例えば、上の例のように再スローした後。もしくは次のようにチェーンの終わりにエラーハンドラを追加し忘れている場合です。: - -```js untrusted run refresh -new Promise(function() { - noSuchFunction(); // ここでエラー(このような関数はない) -}); // .catch は未アタッチ -``` - -もしくは: - -```js untrusted run refresh -// 末尾に .catch のない promise のチェーン -new Promise(function() { - throw new Error("Whoops!"); -}).then(function() { - // ...何か... -}).then(function() { - // ...何か... -}).then(function() { - // ...が、この後に catch はありません! -}); -``` - -エラーの場合、promise のステータスは "rejected" になり、実行は最も近い reject ハンドラにジャンプするはずです。しかし上の例ではそのようなハンドラはありません。そのため、エラーは "スタック" します(行き詰まります)。 - -実際には、それは通常悪いコードによるものです。 確かになぜエラー処理がないのでしょうか? - -多くの JavaScript エンジンはこのような状況を追跡し、その場合にはグローバルエラーを生成します。コンソールで見ることができます。 - -ブラウザでは、イベント `unhandledrejection` を使ってキャッチできます。: - -```js run -*!* -window.addEventListener('unhandledrejection', function(event) { - // イベントオブジェクトは2つの特別なプロパティを持っています: - alert(event.promise); // [object Promise] - エラーを生成した promise - alert(event.reason); // Error: Whoops! - 未処理のエラーオブジェクト -}); -*/!* - -new Promise(function() { - throw new Error("Whoops!"); -}); // エラーを処理する catch がない -``` - -そのイベントは [HTML 標準](https://html.spec.whatwg.org/multipage/webappapis.html#unhandled-promise-rejections) の一部です。今、エラーが発生し `.catch` がない場合、`unhandledrejection` ハンドラがトリガします: `event` オブジェクトはエラーに関する情報を持っているので、何かをすることができます。 - -通常、このようなエラーはリカバリ不可なので、最善の方法はユーザにその問題を知らせ、サーバへそのインシデントについて報告することです。 - -Node.JSのようなブラウザ以外の環境では、未処理のエラーを追跡する他の同様の方法があります。 - -## サマリ - -要約すると、`.then/catch(handler)` はハンドラが何をするかによって変化する新しい promise を返します: - -1. もし値を返したり、`return` (`return undefined` と同じ) なしで終了した場合、新しい promise が resolve になり、最も近い resolve ハンドラ(`.then` の最初の引数)がその値で呼ばれます。 -2. もしエラーをスローした場合、新しい promise は reject になり、最も近い reject ハンドラ(`.then` または `.catch` の2つ目の引数)がそれと一緒に呼ばれます。 -3. もし promise を返す場合、JavaScript はそれが完了するまで待機し、同じ方法で結果に作用します。 - -`.then/catch` によって返される promise がどのように変化するかの図です: - -![](promise-handler-variants.png) - -どのようなハンドラが呼ばれるかの小さな図です: - -![](promise-handler-variants-2.png) - -上のエラー処理の例では、`.catch` は常にチェーンの最後でした。実際には、すべての promise チェーンが `.catch` を持っている訳ではありません。通常のコードと同じように、常に `try..catch` でラップされている訳ではありません。 - -エラーを処理したい/エラーを処理する方法を知りたい場所に `.catch` を置くべきです。カスタムエラークラスを使用すると、エラーを分析し、処理できないエラー再スローすることができます。 - -範囲外のエラーについては、 `unhandledrejection` イベントハンドラ(ブラウザ用、および他の環境用の類似物)を用意する必要があります。 このような不明なエラーは通常は回復不可能なので、ユーザーに知らせて、おそらくサーバーにそのインシデントについて報告するだけです。 diff --git a/6-async/03-promise-chaining/promise-handler-variants.png b/6-async/03-promise-chaining/promise-handler-variants.png deleted file mode 100644 index 541c18eb29..0000000000 Binary files a/6-async/03-promise-chaining/promise-handler-variants.png and /dev/null differ diff --git a/6-async/03-promise-chaining/promise-handler-variants@2x.png b/6-async/03-promise-chaining/promise-handler-variants@2x.png deleted file mode 100644 index 3a2f6b1d8b..0000000000 Binary files a/6-async/03-promise-chaining/promise-handler-variants@2x.png and /dev/null differ diff --git a/6-async/03-promise-chaining/promise-then-chain.png b/6-async/03-promise-chaining/promise-then-chain.png deleted file mode 100644 index 7cd1baa93c..0000000000 Binary files a/6-async/03-promise-chaining/promise-then-chain.png and /dev/null differ diff --git a/6-async/03-promise-chaining/promise-then-chain@2x.png b/6-async/03-promise-chaining/promise-then-chain@2x.png deleted file mode 100644 index 3d981342bd..0000000000 Binary files a/6-async/03-promise-chaining/promise-then-chain@2x.png and /dev/null differ diff --git a/6-async/03-promise-chaining/promise-then-many.png b/6-async/03-promise-chaining/promise-then-many.png deleted file mode 100644 index 199867f841..0000000000 Binary files a/6-async/03-promise-chaining/promise-then-many.png and /dev/null differ diff --git a/6-async/03-promise-chaining/promise-then-many@2x.png b/6-async/03-promise-chaining/promise-then-many@2x.png deleted file mode 100644 index 2e7847eb2a..0000000000 Binary files a/6-async/03-promise-chaining/promise-then-many@2x.png and /dev/null differ diff --git a/6-async/04-promise-api/article.md b/6-async/04-promise-api/article.md deleted file mode 100644 index a777e87e78..0000000000 --- a/6-async/04-promise-api/article.md +++ /dev/null @@ -1,202 +0,0 @@ -# Promise API - -`Promise` クラスには 4 つの静的メソッドがあります。ここではそのユースケースについて簡単に説明します。 - -## Promise.resolve - -構文: - -```js -let promise = Promise.resolve(value); -``` - -指定された `value` で解決(resolve)された promise を返します。 - -次と同じです: - -```js -let promise = new Promise(resolve => resolve(value)); -``` - -このメソッドはすでに値を持っているが、promise で "ラップ" したい場合に使われます。 - -例えば、下の `loadCached` 関数は `url` をフェッチし、将来の同じ URL 呼び出しですぐに結果を返すために、それを覚えます。: - -```js -function loadCached(url) { - let cache = loadCached.cache || (loadCached.cache = new Map()); - - if (cache.has(url)) { -*!* - return Promise.resolve(cache.get(url)); // (*) -*/!* - } - - return fetch(url) - .then(response => response.text()) - .then(text => { - cache[url] = text; - return text; - }); -} -``` - -`loadCached(url).then(…)` を使うことができます。なぜなら、この関数は promise を返すことを保証しているからです。それが、行 `(*)` の `Promise.resolve` の目的です: インタフェースを統一します。これにより、`loadCached` の後で常に `.then` が使えます。 - -## Promise.reject - -構文: - -```js -let promise = Promise.reject(error); -``` - -`error` で拒否(reject)された promise を生成します。 - -次と同じです: - -```js -let promise = new Promise((resolve, reject) => reject(error)); -``` - -ここでは完全性のために説明しますが、実際のコードではほとんど使われません。 - -## Promise.all - -並列に複数の promise を実行し、すべてが準備できるまで待つためのメソッドです。 - -構文は次の通りです: - -```js -let promise = Promise.all(iterable); -``` - -これは promise を持つ `iterable(反復可能な)` なオブジェクトを取ります。技術的には任意の iterable が可能ですが、通常は配列であり、新しい promise を返します。新しい promise はそれらのすべてが解決され、結果を配列に持ったときに解決されます。 - -例えば、下の `Promise.all` は 3 秒後に解決され、その後結果は配列 `[1, 2, 3]` です。: - -```js run -Promise.all([ - new Promise((resolve, reject) => setTimeout(() => resolve(1), 3000)), // 1 - new Promise((resolve, reject) => setTimeout(() => resolve(2), 2000)), // 2 - new Promise((resolve, reject) => setTimeout(() => resolve(3), 1000)) // 3 -]).then(alert); // 1,2,3 promise が準備できた時: 各 promise は配列の中身に寄与します -``` - -相対的な順序は同じであることに注意してください。たとえ1つ目の promise が解決まで最も時間がかかったとしても、結果の配列の中では依然として先頭です。 - -一般的なやり方は、ジョブデータの配列を promise の配列にマップし、それを `Promise.all` にラップすることです。 - -例えば、URL の配列を持っている場合、次のようにしてフェッチできます: - -```js run -let urls = [ - 'https://api.github.com/users/iliakan', - 'https://api.github.com/users/remy', - 'https://api.github.com/users/jeresig' -]; - -// 各 url を promise の fetch(github url) へマップする -let requests = urls.map(url => fetch(url)); - -// Promise.all はすべてのジョブが解決されるまで待ちます -Promise.all(requests) - .then(responses => responses.forEach( - response => alert(`${response.url}: ${response.status}`) - )); -``` - -github ユーザ名の配列に対して(または id の配列でも可能です。ロジックは同じです)、ユーザ情報を取得するより実践的な例です。: - -```js run -let names = ['iliakan', 'remy', 'jeresig']; - -let requests = names.map(name => fetch(`https://api.github.com/users/${name}`)); - -Promise.all(requests) - .then(responses => { - // すべてのレスポンスが用意できたら HTTP ステータスコードが見れます - for(let response of responses) { - alert(`${response.url}: ${response.status}`); // 各 url で 200 が表示されます - } - - return responses; - }) - // それぞれの中身を読むために、レスポンスの配列を response.json() の配列にマッピングします - .then(responses => Promise.all(responses.map(r => r.json()))) - // すべての JSON応答が解析され、"user" はそれらの配列です。 - .then(users => users.forEach(user => alert(user.name))); -``` - -いずれかの promise が reject された場合、`Promise.all` は即座にエラーと一緒に reject します。 - -例: - - -```js run -Promise.all([ - new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)), -*!* - new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)), -*/!* - new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)) -]).catch(alert); // Error: Whoops! -``` - -ここでは、2つ目の promise が2秒後に reject されます。それは即座に `Promise.all` の reject に繋がるため、`catch` が実行されます。: reject されたエラーは `Promise.all` 全体の結果になります。 - -重要な点は、promise は実行を "キャンセル" したり "廃止" する方法は提供していないということです。したがって、他の promise は実行を続け、最終的に解決されますが、それらの結果は無視されます。 - -これを回避する方法があります: エラーが発生した場合、 promise を `clearTimeout` (またはキャンセル)するために追加のコード書く、またはエラーを結果の配列の中のメンバとして表示させる、です(これについてはこのチャプターの下にタスクを見てください)。 - -````smart header="`Promise.all(iterable)` は `iterable` の中で非 promise の項目を許可します" -通常、`Promise.all(iterable)` は promise の iterable (ほとんどの場合は配列)を受け付けます。しかし、もしそれらのオブジェクトが promise ではない場合、`Promise.resolve` でラップします。 - -例えば、ここでは結果は `[1, 2, 3]` になります: - -```js run -Promise.all([ - new Promise((resolve, reject) => { - setTimeout(() => resolve(1), 1000) - }), - 2, // Promise.resolve(2) と扱われる - 3 // Promise.resolve(3) と扱われる -]).then(alert); // 1, 2, 3 -``` - -したがって、私たちは便宜的な場合に、非 promise の値を `Promise.all` に渡すことができます。 - -```` - -## Promise.race - -これは `Promise.all` と同様に promise の iterable を取りますが、すべてが完了するのを待つ代わりに -- 最初の結果(またはエラー)を待ち、続けます。 - -構文です: - -```js -let promise = Promise.race(iterable); -``` - -例えば、ここでは結果は `1` になります: - -```js run -Promise.race([ - new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)), - new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)), - new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)) -]).then(alert); // 1 -``` - -なので、最初の結果/エラーが `Promise.race` 全体の結果になります。最初の確定した promise が "競争に勝った" 後、それ以外の結果/エラーは無視されます。 - -## サマリ - -`Promise` クラスには 4 つの静的なメソッドがあります。: - -1. `Promise.resolve(value)` -- 与えられた値で promise を解決(resolve)します -2. `Promise.reject(error)` -- 与えられたエラーで promise を拒否(reject)します -3. `Promise.all(promises)` -- すべての promise が解決するのを待って、結果の配列を返します。 与えられた promise が拒否されると、それは `Promise.all` のエラーになり、他のすべての結果は無視されます。 -4. `Promise.race(promises)` -- 最初の promise の解決を待ち、その結果/エラーが結果になります。 - -これら4つのうち、`Promise.all` が実際には最も一般的です。 diff --git a/6-async/05-async-await/01-rewrite-async/solution.md b/6-async/05-async-await/01-rewrite-async/solution.md deleted file mode 100644 index 506fe39e2d..0000000000 --- a/6-async/05-async-await/01-rewrite-async/solution.md +++ /dev/null @@ -1,33 +0,0 @@ - -補足はコードの下にあります: - -```js run -async function loadJson(url) { // (1) - let response = await fetch(url); // (2) - - if (response.status == 200) { - let json = await response.json(); // (3) - return json; - } - - throw new Error(response.status); -} - -loadJson('no-such-user.json') - .catch(alert); // Error: 404 (4) -``` - -補足: - -1. 関数 `loadUrl` は `async` になります。 -2. すべての内側の `.then` は `await` に置き換えられます。 -3. 次のように、await するのではなく、`response.json()` を返すこともできます。: - - ```js - if (response.status == 200) { - return response.json(); // (3) - } - ``` - - そうすると、外側のコードはその promise を解決するために `await` する必要があります。 -4. `loadJson` からスローされたエラーは `.catch` で処理されます。そこでは `await loadJson(…)` を使うことができません。なぜなら `async` 関数の中ではないからです。 diff --git a/6-async/05-async-await/article.md b/6-async/05-async-await/article.md deleted file mode 100644 index dbf36ee800..0000000000 --- a/6-async/05-async-await/article.md +++ /dev/null @@ -1,299 +0,0 @@ -# Async/await - -"async/await" と呼ばれる、より快適に promise を利用する特別な構文があります。驚くほど簡単に理解し、使用することができます。 - -## Async 関数 - -`async` キーワードから始めましょう。次のように関数の前に置くことができます: - -```js -async function f() { - return 1; -} -``` - -関数の前の用語 "async" は1つの単純なことを意味します: 関数は常に promise を返します。コード中に `return <非 promise>` がある場合、JavaScript は自動的にその値を持つ 解決された promise にラップします。 - -例えば、上のコードは 結果 `1` を持つ解決された promise を返します。テストしてみましょう: t it: - -```js run -async function f() { - return 1; -} - -f().then(alert); // 1 -``` - -...明示的に promise を返すこともでき、それは同じです: - -```js run -async function f() { - return Promise.resolve(1); -} - -f().then(alert); // 1 -``` - -したがって、`async` は関数が promise を返すことを保証し、非promise をその中にラップします。シンプルですよね?しかし、それだけではありません。`async` 関数の中でのみ動作する別のキーワード `await` があります。これはとてもクールです。 - -## Await - -構文: - -```js -// async 関数の中でのみ動作します -let value = await promise; -``` - -キーワード `await` は promise が確定しその結果を返すまで、JavaScript を待機させます。 ult. - -これは1秒で解決する promise の例です: -```js run -async function f() { - - let promise = new Promise((resolve, reject) => { - setTimeout(() => resolve("done!"), 1000) - }); - -*!* - let result = await promise; // promise が解決するまで待ちます (*) -*/!* - - alert(result); // "done!" -} - -f(); -``` - -関数の実行は行 `(*)` で "一時停止" し、promise が確定したときに再開し、`result` がその結果になります。 そのため、上のコードは1秒後に "done!" を表示します。 - -`await` は文字通り promise が確定するまで JavaScript を待ってから、その結果で続くことに注目しましょう。その間、エンジンは他のジョブ(他のスクリプトを実行し、イベントを処理するなど)を実行することができるため、CPUリソースを必要としません。 - -これは、`promise.then` よりも promise の結果を得るためのより洗練された構文です。読みやすく、書くのが簡単です。 - -````warn header="通常の関数で `await` を使うことはできません" -非async関数で `await` を使おうとした場合、構文エラーになります。: - -```js run -function f() { - let promise = Promise.resolve(1); -*!* - let result = await promise; // Syntax error -*/!* -} -``` - -関数の前に `async` を置き忘れた場合にこのエラーが発生します。先程言ったように、`await` は `async function` の中でのみ動作します。 -```` - -チャプター <info:promise-chaining> から例 `showAvatar()` を取り、`async/await` を使って書き直してみましょう: - -1. `.then` 呼び出しを `await` に置き換える必要があります。 -2. また、機能させるために関数を `async` にする必要があります。 - -```js run -async function showAvatar() { - - // JSON を読み込む - let response = await fetch('/article/promise-chaining/user.json'); - let user = await response.json(); - - // github ユーザを読み込む - let githubResponse = await fetch(`https://api.github.com/users/${user.name}`); - let githubUser = await githubResponse.json(); - - // アバターを表示する - let img = document.createElement('img'); - img.src = githubUser.avatar_url; - img.className = "promise-avatar-example"; - document.body.append(img); - - // 3秒待つ - await new Promise((resolve, reject) => setTimeout(resolve, 3000)); - - img.remove(); - - return githubUser; -} - -showAvatar(); -``` - -非常にスッキリし、読みやすいですよね?前よりもはるかに良いです。 - -````smart header="`await` はトップレベルのコードでは動作しません" -`await` を使い始めたばかりの人は忘れる傾向がありますが、トップレベルのコードで `await` を書くことはできません。それはうまく行きません: - -```js run -// トップレベルのコードでは構文エラー -let response = await fetch('/article/promise-chaining/user.json'); -let user = await response.json(); -``` - -なので、await をするコードに対しては async 関数をラップする必要があります。ちょうど上の例で示しているように。 -```` -````smart header="`await` は thenable を許容します" -`promise.then` のように、`await` は thenable オブジェクト(`then` メソッドを呼ぶことができるもの)を使うことができます。繰り返しになりますが、このアイデアは、サードパーティオブジェクトは promise ではなく、promise 互換である場合があるということです(`.then` をサポートしている場合、`await` で使えます)。 - -例えば、ここで `await` は `new Thenable(1)` を許容します: -```js run -class Thenable { - constructor(num) { - this.num = num; - } - then(resolve, reject) { - alert(resolve); // function() { native code } - // 1000ms 後に this.num*2 で解決する - setTimeout(() => resolve(this.num * 2), 1000); // (*) - } -}; - -async function f() { - // 1秒待って、結果は 2 になる - let result = await new Thenable(1); - alert(result); -} - -f(); -``` - -もし `await` が `.then` で非promise オブジェクトを得た場合、引数としてネイティブ関数 `resolve`, `reject` を提供しているメソッドを呼び出します。次に `await` はいずれかが呼ばれるまで待ち(上の例では、行 `(*)` です)、その結果で進みます。 - -```` - -````smart header="Async メソッド" -クラスメソッドもまた asyne になれます。ただ前に `async` を置くだけです。 - -Like here: - -```js run -class Waiter { -*!* - async wait() { -*/!* - return await Promise.resolve(1); - } -} - -new Waiter() - .wait() - .then(alert); // 1 -``` -意味は同じです: 返却される値が promise であることを保証し、`await` を有効にします。 - -```` -## エラー処理 - -もし promise が正常に解決すると、`await promise` は結果を返します。しかし拒否(reject) の場合はエラーをスローします。それはちょうどその行に `throw` 文があるように振る舞います。 - -このコードは: - -```js -async function f() { -*!* - await Promise.reject(new Error("Whoops!")); -*/!* -} -``` - -...これと同じです: - -```js -async function f() { -*!* - throw new Error("Whoops!"); -*/!* -} -``` - -実際には、promise を拒否するまでに時間がかかる場合があります。なので、`await` は待ち、その後エラーをスローします。 - -エラーは `try..catch` でキャッチすることができ、それは通常の `throw` と同じ方法です: - -```js run -async function f() { - - try { - let response = await fetch('http://no-such-url'); - } catch(err) { -*!* - alert(err); // TypeError: failed to fetch -*/!* - } -} - -f(); -``` - -エラーの場合、コントロールは `catch` ブロックにジャンプします。複数行をラップすることも可能です: - -```js run -async function f() { - - try { - let response = await fetch('/no-user-here'); - let user = await response.json(); - } catch(err) { - // fetch と response.json 両方のエラーをキャッチ - alert(err); - } -} - -f(); -``` - -もし `try..catch` がない場合、async 関数 `f()` の呼び出しによって生成された promise は拒否されます。それを処理にするには `.catch` を追加します。: - -```js run -async function f() { - let response = await fetch('http://no-such-url'); -} - -// f() は拒否された promise になる -*!* -f().catch(alert); // TypeError: failed to fetch // (*) -*/!* -``` - -そこに `.catch` を追加し忘れると、未処理の promise エラーを得ます(それはコンソールで見えます)。チャプター <info:promise-chaining> で説明した通り、グローバルイベントハンドラを使用することでこのようなエラーをキャッチすることができます。 - - -```smart header="`async/await` と `promise.then/catch`" -`async/await` を使用するとき、`.then` はほとんど必要がありません。なぜなら `await` は私たちを待っているからです。そして `.catch` の代わりに通常の `try..catch` を使うことができます。それは通常(常にではないですが)より便利です。 - -しかし、コードの最上位のレベルでは、`async` 関数の外にいるときは構文的に `await` を使うことができないため、最終的な結果または落ちるようなエラーを処理するために `.then/catch` を追加するのが普通です。 - -上の例の行 `(*)` のように。 -``` - -````smart header="`async/await` は `Promise.all` とうまく動作します" -複数の promise を待つ必要があるとき、`Promise.all` でラップしてから `await` できます。 - -```js -// 結果の配列をまつ -let results = await Promise.all([ - fetch(url1), - fetch(url2), - ... -]); -``` - -エラーが発生した場合、それは通常通り伝搬します: 失敗した promise から `Promise.all` に伝播し、呼び出しのまわりで `try..catch` を使ってキャッチができる例外になります。 - -```` - -## サマリ - -関数の前の `async` キーワードは2つの効果があります: - -1. 常に promise を返します -2. その中で `await` を使えるようにします - -promise の前の `await` キーワードは、 promise が確定するまで JavaScript を待たせ、次のことをします: - -1. それがエラーであれば、まさにその場所で `throw error` が呼び出されたのと同じ例外が生成されます。 -2. それ以外の場合は、結果を返すので、値に割り当てることができます。 - -共に、読み書きするのが簡単な非同期コードを書くことができる素晴らしいフレームワークを提供します。 - -`async/await` と一緒に `promise.then/catch` を書く必要はほとんどありませんが、時には(例えば最も外側のスコープで)これらのメソッドを使わなければならないことがあるので、これらが promise に基づいていることを忘れてはいけません。 また、`Promise.all` は同時に多くのタスクを待つ良い方法です。 diff --git a/6-async/index.md b/6-async/index.md deleted file mode 100644 index a6ec54397b..0000000000 --- a/6-async/index.md +++ /dev/null @@ -1,2 +0,0 @@ - -# Promises, async/await diff --git a/6-data-storage/01-cookie/article.md b/6-data-storage/01-cookie/article.md new file mode 100644 index 0000000000..34fa5bc53f --- /dev/null +++ b/6-data-storage/01-cookie/article.md @@ -0,0 +1,418 @@ +# Cookies(クッキー), document.cookie + +Cookie はブラウザに直接格納される小さな文字列データです。これらは JavaScript の一部ではなく、[RFC 6265](https://tools.ietf.org/html/rfc6265) 仕様で定義されている HTML プロトコル の一部です。 + +多くの場合、Cookieは Web サーバによって設定されます。 + +Cookie の最も広く使われている用途の1つは認証です: + +1. サインインすると、サーバは Cookie に "セッションID" をセットするために、応答に `Set-Cookie` HTTP ヘッダを使用します。 +2. ブラウザは Cookie を格納します。 +3. 次回、同じドメインへのリクエストが行われるとき、ブラウザは `Cookie` HTTPヘッダを使用してネット上に送信します。 +4. そのため、サーバは誰がリクエストを行ったのかを知ることができます。 + +ブラウザは Cookie のための特別なアクセサ `document.cookie` を提供します。 + +Cookie とそのオプションについては、多くの注意点があります。このチャプターでは、それらの詳細を説明してきます。 + +## document.cookie からの読み込み + +```online +このサイトに Cookie はあるでしょうか?見てみましょう: +``` + +```offline +今Webサイトにいると仮定すると、次のようにすることで Cookie を見ることができます: +``` + +```js run +// javascript.info では統計のために Google Analytics を使用しています, +// そのため、いくつかの Cookie があるはずです +alert( document.cookie ); // cookie1=value1; cookie2=value2;... +``` + +文字列は `name=value` のペアからなり、 `;` により区切られます。それぞれが別の Cookie です。 + +特定の Cookie を見つけるには、`;` で `document.cookie` を分割し、正しい名前を見つけます。それをするのに、正規表現あるいは配列関数が使えます。 + +## document.cookie への書き込み + +`document.cookie` へ書き込む事ができます。しかし、データプロパティではなくアクセサを利用します。 + +**`document.cookie` への書き込み操作はブラウザを通して行われ、そこに記載されている Cookie を更新しますが、他の Cookie には触れません。** + +例えば、この呼び出しは名前が `user` で値が `John` の Cookie をセットします。: + +```js run +document.cookie = "user=John"; // 名前が `user` の Cookie だけを更新します +alert(document.cookie); // すべての Cookie を表示します +``` + +実行すると、おそらく複数の Cookie が見えるでしょう。なぜなら、`document.cookie=` 操作はすべてのクッキーを上書きするのではなく、`user` だけを上書きするからです。 + +技術的には、名前と値は任意の文字が可能ですが、フォーマットを有効に保つためには、組み込みの `encodeURIComponent` 関数を使ってエスケープする必要があります。: + +```js run +// 特別な値, エンコードが必要です +let name = "<>"; +let value = "=" + +// Cookie を %3C%3E=%3D とエンコード +document.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value); + +alert(document.cookie); // ...; %3C%3E=%3D +``` + + +```warn header="制限" +いくつかの制限があります: +- `encodeURIComponent` 後の `name=value` ペアは 4KB を超えてはいけません。なので、 Cookie に大きなデータを格納することはできません。 +- ドメイン毎の Cookie の総数は 20+ で制限されています(正確な数字はブラウザに依存します)。 +``` + +Cookie はいくつかのオプションを持っており、その多くは重要で設定するべきものです。 + +オプションは `key=value` の後にリストされ、`;` で区切られます。次のようになります。: + +```js run +document.cookie = "user=John; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT" +``` + +## path + +- **`path=/mypath`** + +Cookie へアクセス可能な URL パスプレフィックスです。絶対値でなければなりません。デフォルトでは現在のパスになります。 + +Cookie が `path=/mypath` で設定された場合、それは `/mypath` と `/mypath/*` で見えますが、`/page` や `/mypathpage` では見えません。 + +通常は、すべての Webサイトのページから Cookie へアクセスできるよう、`path=/` を設定します。 + +## domain + +- **`domain=site.com`** + +Cookie へアクセス可能なドメインです。 + +デフォルトでは、Cookie はそれを設定したドメインでのみアクセス可能です。そのため、Cookie が `site.com` で設定されている場合、 `other.com` では取得できません。 + +...しかし、より注意が必要なことに、サブドメイン `forum.site.com` でも Cookie は取得できません: + +```js +// at site.com +document.cookie = "user=John" + +// at forum.site.com +alert(document.cookie); // no user +``` + +**別の第2レベルのドメインから Cookie にアクセスさせる方法はありません。そのため、`other.com` が `site.com` で設定された Cookie を受け取ることはありません。** + +これは機密データを Cookie に保持することができるようにするための、安全上の制限です。 + +...ですが、`forum.site.com` などのサブドメインへのアクセスを許可したい場合、それは可能です。その場合には、明示的に `domain` オプションをルートドメインに設定します: `domain=site.com`: + +```js +// site.com で任意のサブドメインで Cookie にアクセスできるようにします +document.cookie = "user=John; domain=site.com" + +// forum.site.com +alert(document.cookie); // with user +``` + +歴史的な理由から、`domain=.site.com` (ドット開始)もこのように機能します。非常に古いブラウザをサポートするにはドットを追加しておくのがよいでしょう。 + +## expires, max-age + +デフォルトでは、Cookie がこれらのどのオプションを持っていない場合、ブラウザが閉じられたときに消えます。このような Cookie は "セッションクッキー" と呼ばれます。 + +ブラウザを閉じても Cookie を生存させるには、`expires` あるいは `max-age` オプションを設定します。 + +- **`expires=Tue, 19 Jan 2038 03:14:07 GMT`** + +ブラウザが自動的に Cookie を削除する、Cookie の有効期限です。 + +日付は GMT タイムゾーンの正確な形式でなければなりません。それは `date.toUTCString` で取得できます。例えば、Cookie の有効期限を1日に設定する場合は次のようになります。: + +```js +// いまから +1 日 +let date = new Date(Date.now() + 86400e3); +date = date.toUTCString(); +document.cookie = "user=John; expires=" + date; +``` + +もし `expires` を過去に設定すると、Cookie は削除されます。 + +- **`max-age=3600`** + +`expires` の代替として、Cookie の有効期限を秒で指定します。 + +現時点からの秒数、または即時有効期限切れの場合(Cookie を削除するために)はゼロ/負の値を指定できます。: + +```js +// cookie は今から1時間後に消えます +document.cookie = "user=John; max-age=3600"; + +// cookie 削除(すぐに有効期限切れにする) +document.cookie = "user=John; max-age=0"; +``` + +## secure + +- **`secure`** + +Cookie は HTTPS 経由でのみ転送するべきです。 + +**デフォルトでは、Cookie を `http://site.com` にセットした場合、それは `https://site.com` にも現れます。そしてその逆もしかりです。** + +つまり、Cookie はドメインのみをチェックしており、プロトコルを区別しません。 + +このオプションでは、Cookie が `https://site.com` でセットされた場合、その後 `http://site.com` のように HTTP で同じサイトにアクセスしても Cookie は現れません。そのため、Cookie に暗号化されていない HTTP 経由で送信されるべきではない機密コンテンツが含まれている場合、このフラグで防ぐ事ができます。 + +```js +// Cookie をセキュアに設定 (HTTPS 経由でのみアクセス可能) +document.cookie = "user=John; secure"; +``` + +## samesite + +これは、いわゆる XSRF (クロスサイトリクエストフォージェリ)攻撃から保護するための、もう1つのセキュリティオプションです。 + +いつ役立つのかを理解するために、次の攻撃シナリオを紹介しましょう。 + +### XSRF 攻撃 + +想像してください、あなたはサイト `bank.com` にログインしました。つまり:あなたはそのサイトからの認証 Cookie を持っています。ブラウザは、あなたを認識して、すべての慎重に扱うべき金融操作を実行するために、リクエスト毎に Cookie を `bank.com` に送信します。 + +いま、別のウィンドウで Web をブラウジングしていると、別のサイト `evil.com` にやってきました。そして、それはハッカーのアカウントを持つ `<form action="https://bank.com/pay">` と、自動的にサブミットする JavaScript コードを持っています。 + +そのフォームは `evil.com` から直接銀行のサイトに送信され、あなたの Cookie も送信されます。なぜなら、あなたが `bank.com` に訪れるたびに送信されるからです。そのため、銀行はあなたを認識し、実際に支払いを実行します。 + +![](cookie-xsrf.svg) + +これはクロスサイトリクエストフォージェリ(または XSRF)攻撃と呼ばれます。 + +もちろん、実際の銀行はそれから保護されています。`bank.com` によって生成されたすべてのフォームは、"xsrf 保護トークン" と呼ばれる特別なフィールドを持っており、悪意のあるページはそれを生成することも、リモートページから何らかの形で抽出することもできません(そこにフォームを送信することはできますが、データを戻すことはできません)。 + +### Cookie samesite オプションを入力する + +いま、Cookie `samesite` オプションはこのような攻撃から保護するためのもう1つの方法を提供します。それは(理論的には)、"xsrf 保護トークン" を必要としません。 + +2つのとり得る値があります: + +- **`samesite=strict`, 値なしの `samesite` と同じです** + +ユーザがサイトの外からきた場合、`samesite=strict` を持つ Cookie は決して送信されません。 + +言い換えると、ユーザがメールにあるリンクを辿った場合や、`evil.com` からのフォームを送信した場合、あるいは他のドメインから生じたサイトに関する任意の操作をした場合、Cookie は送信されません。 +そして、XSRF 攻撃は失敗します。なぜなら、`bank.com` は Cookie がないのでユーザを認識せず、支払いには進めないでしょう。 + +この保護はとても信頼できます。`bank.com` からの操作のみ samesite の Cookie を送信します。 + +しかしながら、多少の不便があります。 + +ユーザが、自身のメモなどから `bank.com` への正当なリンクを辿った際、`bank.com` がそれらを認識しないことに驚くでしょう。確かに、`samesite=strict` Cookie はこのケースでは送信されません。 + +2つのCookieを使って回避することができます: 1つは "一般的な認識" 用で、"Hello, John" というためだけのものです。もう1つは `samesite=strict` を持つデータ変更用のものです。 + +すると、サイトの外から来た人は歓迎("Hello, John")を受けますが、支払いは銀行のWebサイトから始められなければなりません。 + +- **`samesite=lax`** + +ユーザ体験を維持するためのもう1つのアプローチは、より寛容な値である `samesite=lax` を使うことです。 + +Lax モードでは、`strict` のように、サイトの外から来たときにブラウザが Cookie を送信するのを禁止しますが、例外があります。 + +`samesite=lax` の Cookie は、これらの条件が両方とも true の場合に送信されます。: +1. HTTP メソッドが "安全" である(e.g. POST ではなく GET)。 + + 安全なHTTP メソッドの完全なリストは [RFC7231 specification](https://tools.ietf.org/html/rfc7231) にあります。基本的に、これらはデータの読み取りのために使用され、データ書き込みには使用するべきでないメソッドです。データ変更操作を実行してはいけません。リンクをたどることは、常にGET(安全なメソッド)です。 + +2. 操作は最上位のナビゲーションで実行される(ブラウザのアドレスバーの URL を変更する) + + これは通常 true ですが、ナビゲーションが `<iframe>` で実行された場合、これは最上位ではありません。また、AJAX リクエストはどのナビゲーションも行わないため、この条件にはマッチしません。 + +したがって、`samesite=lax` が行うことは、基本的に最も一般的な "URL を開く" という操作で Cookie を利用できるようにすることです。他のサイトからのAJAXリクエストやフォーム送信など、より複雑なことをするときには Cookie を失います。 + +それで問題ないのであれば、`samesite=lax` を追加してもおそらくユーザー体験を損なうことはなく、保護を追加できるでしょう。 + +全体的に見て、`samesite` は素晴らしいですが、重要な欠点があります。: +- 古いブラウザ(2017年あたり) では `samesite` はサポートされておらず、無視されます。 + +**そのため、保護を提供するために `samesite` だけに頼った場合、古いブラウザは完全に脆弱になるでしょう。** + +しかし、xsrf トークンなど他の保護手段と合わせて `samesite` を使用し、追加の防御層を追加することができます。 + +## httpOnly + +このオプションは JavaScript とは関係ありませんが、ここでは完全性のために言及する必要があります。 + +サーバが Cookie を設定するために `Set-Cookie` を使うとき、`httpOnly` オプションを設定することができます。 + +このオプションは、任意のJavaScript が Cookie へアクセスすることを禁止します。我々は、`document.cookie` を使ってこのような Cookie を見たり操作することはできません。 + +これは、ハッカーが自分の JavaScript コードをページに挿入し、ユーザがそのページに訪問するのを待つといった特定の攻撃から保護するための予防策として使われます。ハッカーが我々のサイトにコードを挿入することができるべきではありませんが、ハッカーにそれをさせてしまうバグがあるかもしれません。 + +通常、そのようなことが起き、ユーザがハッカーのコードが含まれた Web ページを訪れると、そのコードは実行され、認証情報を含むユーザの Cookie を持つ `document.cookie` へのアクセスを得ます。それは良くありません。 + +しかし、Cookie が `httpOnly` であれば、`document.cookie` にそれは見えないため、守られます。 + +## 付録: Cookie 関数 + +ここにあるのは、Cookie を扱うための関数の小さなセットです。手動で `docment.cookie` を変更するよりもはるかに便利です。 + +そのための Cookie ライブラリは数多くありますので、これらは完全に動作しますが、デモ用です。: + + +### getCookie(name) + +Cookie にアクセスする最も早い方法は [正規表現](info:regular-expressions) を使うことです。 + +関数 `getCookie(name)` は指定された `name` の Cookie を返します。: + +```js +// 指定された name を持つ Cookie を返します +// なければ undefined を返します +function getCookie(name) { + let matches = document.cookie.match(new RegExp( + "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)" + )); + return matches ? decodeURIComponent(matches[1]) : undefined; +} +``` + +ここでは、この正規表現は `; name=<value>` にマッチするよう動的に生成されます。 + +Cookie の値は任意の文字列にすることができることに留意してください。もしフォーマットを破る文字が含まれている場合(例えばスペースや `;` です)、このような文字はエンコードされます。 + +デコードするには、組み込みの `decodeURIComponent` 関数を使う必要があります。 + +### setCookie(name, value, options) + +デフォルトでは、Cookie `name` を `path=/` を持つ指定された `value` に設定します(他のデフォルトを追加するよう変更することができます)。 + +```js run +function setCookie(name, value, options = {}) { + + options = { + path: '/', + // 必要でれば他のデフォルトを追加する + ...options + }; + + if (options.expires.toUTCString) { + options.expires = options.expires.toUTCString(); + } + + let updatedCookie = encodeURIComponent(name) + "=" + encodeURIComponent(value); + + for (let optionKey in options) { + updatedCookie += "; " + optionKey; + let optionValue = options[optionKey]; + if (optionValue !== true) { + updatedCookie += "=" + optionValue; + } + } + + document.cookie = updatedCookie; +} + +// 使用例: +setCookie('user', 'John', {secure: true, 'max-age': 3600}); +``` + +### deleteCookie(name) + +Cookie を削除するためには、負の有効期限でそれを呼びます。: + +```js +function deleteCookie(name) { + setCookie(name, "", { + 'max-age': -1 + }) +} +``` + +```warn header="更新または削除は同じパスとドメインを使用する必要があります" +注意: Cookie の更新/削除をするとき、セットしたときと正確に同じパスとドメインオプションを使用する必要があります。 +``` + +一つにまとめたものです: [cookie.js](cookie.js). + + +## 付録: サードパーティ Cookie + +ユーザが訪れているドメイン以外のドメインに置かれた Cookie は "サードパーティ" と呼ばれます。 + +例: +1. `site.com` のページは別のサイトからバナーをロードします。: `<img src="https://ads.com/banner.png">` +2. バナーと一緒に、`ads.com` のリモートサーバは `id=1234` のような Cookie を持つ `Set-Cookie` ヘッダをセットするかもしれません。このような Cookie は `ads.com` ドメインから発生し、`ads.com` でのみ見えます。: + + ![](cookie-third-party.svg) + +3. 次回 `ads.com` にアクセスがあったとき、リモートサーバは `id` Cookie を取得し、ユーザを認識します。: + + ![](cookie-third-party-2.svg) + +4. さらに重要なことは、ユーザ `site.com` から同じくバナーをもつ別のサイト `other.com` に移動するとき、`ads.com` に属しているので、`ads.com` は Cookie を受け取ります。このように、訪問者を認識し、彼らがサイト間を移動するのを追跡します。: + + ![](cookie-third-party-3.svg) + + +サードパーティ Cookie はその性質上、昔からトラッキングや広告サービスに使われています。それらはオリジナルのドメインにバインドされているので、`ads.com` は異なるサイト間で同一ユーザを追跡することができます。 + +当然、追跡されることを好まない人もいるので、ブラウザはこのような Cookie を無効にすることが可能です。 + +また、いくつかのモダンブラウザはこのような Cookie 用に特別なポリシーを採用しています。: +- Safari はサードパーティ Cookie を一切許可しません。 +- Firefox はサードパーティ Cookie をブロックするサードパーティドメインの "ブラックリスト" を付属しています。 + + +```smart +もし、`<script src="https://google-analytics.com/analytics.js">` のようなサードパーティドメインからスクリプトを読み込み、そのスクリプトが Cookie をセットするために `document.cookie` を使った場合、そのような Cookie はサードパーティ Cookie ではありません。 + +スクリプトが Cookie を設定した場合、そのスクリプトがどこから来たかは関係ありません。それは現在の Web ページのドメインに属します。 +``` + +## 付録: GDPR + +このトピックは JavaScript とはまったく関係ありません。単に Cookie を設定するときに心に留めておくことです。 + +ヨーロッパには GDPR と呼ばれる法律があり、それは、ユーザのプライバシーを尊重するために Web サイトに対してあるルールを強制しています。そして、このような規則の1つに、追跡をする Cookie に対してはユーザから明示的な許可を要求する、という内容があります。 + +注意してください。これは、追跡/識別 Cookie についてのみです。 + +なので、単に情報を保持するだけで、ユーザの追跡も識別もしない Cookie の設定は自由に行う事ができます。 + +しかし、認証セッションや追跡id といった情報を Cookie に設定する場合には、ユーザはそれを許可する必要があります。 + +Web サイトは一般的に GDPR に沿った2つのバリアントを持っています。あなたは web 上でそれら両方を見たことがあるに違いません。: + +1. もし web サイトが認証されたユーザに対してのみ追跡 Cookie を設定したい場合。 + + そうするためには、登録フォームに "プライバシーポリシーに同意する" のようなチェックボックスをもたせ、ユーザはそれをチェックする必要があります。それ以降、web サイトは認証 Cookie を自由にセットできます。 + +2. もし web サイトが全員に対して追跡 Cookie を設定したい場合 + + 合法的にそうするためには、web サイトは新しく来た訪問者のためのモーダル "スプラッシュスクリーン" を表示し、彼らに Cookie に関して同意することを要求します。その後、web サイトは Cookie をセットすることができ、訪問者にコンテンツを見せます。これは新規の訪問者にとっては面倒なことです。コンテンツの代わりに "必ずクリック" モーダルを見たい人はいません。ですが、GDPR は明確な同意を要求します。 + +GDPR は Cookie だけでなく、他のプライバシーに関連する問題についても同様です。が、それは我々のスコープ外です。 + + +## サマリ + +`document.cookie` は Cookie へのアクセスを提供します +- 書き込み操作は、言及された Cookie のみを変更します。 +- name/value はエンコードが必要です。 +- 1つのCookie は 4KB までで、サイト毎に Cookie は 20+ です(ブラウザによって異なります) + +Cookie オプション: +- `path=/`, デフォルトでは、現在のパスです。パス配下でのみ Cookie が見えるようにします。 +- `domain=site.com`, デフォルトでは Cookie は現在のドメインでのみ見えます。ドメインが明示的に設定されている場合、サブドメイン上でも Cookie が見えます。 +- `expires/max-age`, Cookie の有効期限を設定します。ない場合、Cookie はブラウザが閉じられたときに消えます。 +- `secure`, Cookie を HTTPS のみにします。 +- `samesite`, ブラウザがサイトの外から来たリクエストで Cookie を送るのを禁止します。XSRF 攻撃を防ぐのに役立ちます。 + +加えて: +- サードパーティ Cookie はブラウザによっては禁止されている場合があります。e.g. Safari はデフォルトではそうです。 +- EU 市民に対して追跡 Cookie を設定する際、GDPR は許可を求めることを要求します。 diff --git a/6-data-storage/01-cookie/cookie-third-party-2.svg b/6-data-storage/01-cookie/cookie-third-party-2.svg new file mode 100644 index 0000000000..8912e4a68f --- /dev/null +++ b/6-data-storage/01-cookie/cookie-third-party-2.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="668" height="192" viewBox="0 0 668 192"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="data-storage" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="cookie-third-party-2.svg"><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M38 48h282v99H38z"/><text id="<img-src="https://ad" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="42" y="70"><img src="https://ads.com/banner.png"></tspan></text><text id="site.com" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="149" y="36">site.com</tspan></text><path id="Rectangle-1-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M505 48h140v99H505z"/><text id="ads.com" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="543" y="36">ads.com</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M321.643 67.375l.991.134 163.127 22.119.806-5.946L499.5 92.5l-14.814 5.055.806-5.945-163.126-22.12-.991-.133.268-1.982z"/><text id="GET-/banner.png" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" transform="rotate(7 416.568 68.382)"><tspan x="361.068" y="73.882">GET /banner.png</tspan></text><text id="cookie:-id=123" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" transform="rotate(7 410.08 93.2)"><tspan x="363.08" y="98.699">cookie: id=123</tspan></text></g></g></svg> \ No newline at end of file diff --git a/6-data-storage/01-cookie/cookie-third-party-3.svg b/6-data-storage/01-cookie/cookie-third-party-3.svg new file mode 100644 index 0000000000..ab38ea27fc --- /dev/null +++ b/6-data-storage/01-cookie/cookie-third-party-3.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="668" height="192" viewBox="0 0 668 192"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="data-storage" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="cookie-third-party-3.svg"><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M38 48h282v99H38z"/><text id="<img-src="https://ad" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="42" y="70"><img src="https://ads.com/banner.png"></tspan></text><text id="other.com" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="144" y="36">other.com</tspan></text><path id="Rectangle-1-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M505 48h140v99H505z"/><text id="ads.com" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="543" y="36">ads.com</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M321.643 67.375l.991.134 163.127 22.119.806-5.946L499.5 92.5l-14.814 5.055.806-5.945-163.126-22.12-.991-.133.268-1.982z"/><text id="GET-/banner.png" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" transform="rotate(7 416.568 68.382)"><tspan x="361.068" y="73.882">GET /banner.png</tspan></text><text id="cookie:-id=123" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" transform="rotate(7 410.08 93.2)"><tspan x="363.08" y="98.699">cookie: id=123</tspan></text><ellipse id="Oval-6" cx="176" cy="31.5" stroke="#C06334" stroke-width="2" rx="48" ry="13.5"/></g></g></svg> \ No newline at end of file diff --git a/6-data-storage/01-cookie/cookie-third-party.svg b/6-data-storage/01-cookie/cookie-third-party.svg new file mode 100644 index 0000000000..21c7cf94d0 --- /dev/null +++ b/6-data-storage/01-cookie/cookie-third-party.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="668" height="192" viewBox="0 0 668 192"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="data-storage" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="cookie-third-party.svg"><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M38 48h282v99H38z"/><text id="<img-src="https://ad" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="42" y="70"><img src="https://ads.com/banner.png"></tspan></text><text id="site.com" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="149" y="36">site.com</tspan></text><path id="Rectangle-1-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M505 48h140v99H505z"/><text id="ads.com" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="543" y="36">ads.com</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M321.643 64.375l.991.134 163.127 22.119.806-5.946L499.5 89.5l-14.814 5.055.806-5.945-163.126-22.12-.991-.133.268-1.982z"/><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M500.366 105.882l.252 1.984-.992.126-163.112 20.734.757 5.953L322.5 129.5l13.006-8.71.755 5.952 163.113-20.734.992-.126z"/><text id="GET-/banner.png" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" transform="rotate(7 416.568 65.382)"><tspan x="361.068" y="70.882">GET /banner.png</tspan></text><text id="Set-Cookie:-id=123" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" transform="rotate(-7 413.469 128.991)"><tspan x="352.969" y="134.491">Set-Cookie: id=123</tspan></text></g></g></svg> \ No newline at end of file diff --git a/6-data-storage/01-cookie/cookie-xsrf.svg b/6-data-storage/01-cookie/cookie-xsrf.svg new file mode 100644 index 0000000000..961a8078f7 --- /dev/null +++ b/6-data-storage/01-cookie/cookie-xsrf.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="668" height="166" viewBox="0 0 668 166"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="data-storage" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="cookie-xsrf.svg"><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M38 48h282v88H38z"/><text id="<form-action="https:" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="48" y="69"><form action="https://bank.com/pay"></tspan> <tspan x="48" y="88"> ....</tspan> <tspan x="48" y="107"></form></tspan></text><text id="evil.com" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="149" y="36">evil.com</tspan></text><path id="Rectangle-1-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M505 48h140v88H505z"/><text id="got-the-cookie?-okay" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal"><tspan x="526.469" y="108">got the cookie?</tspan> <tspan x="558.308" y="127">okay!</tspan></text><text id="bank.com" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="543" y="36">bank.com</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M321.643 67.375l.991.134 163.127 22.119.806-5.946L499.5 92.5l-14.814 5.055.806-5.945-163.126-22.12-.991-.133.268-1.982z"/><text id="POST-/pay" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" transform="rotate(7 409.576 67.26)"><tspan x="376.076" y="72.76">POST /pay</tspan></text><text id="cookie:-user=John" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="14" font-weight="normal" transform="rotate(7 407.076 90.26)"><tspan x="349.076" y="95.76">cookie: user=John</tspan></text></g></g></svg> \ No newline at end of file diff --git a/6-data-storage/01-cookie/cookie.js b/6-data-storage/01-cookie/cookie.js new file mode 100644 index 0000000000..7d8eebf3e6 --- /dev/null +++ b/6-data-storage/01-cookie/cookie.js @@ -0,0 +1,38 @@ +function getCookie(name) { + let matches = document.cookie.match(new RegExp( + "(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)" + )); + return matches ? decodeURIComponent(matches[1]) : undefined; +} + +function setCookie(name, value, options = {}) { + + options = { + path: '/', + // add other defaults here if necessary + ...options + }; + + if (options.expires.toUTCString) { + options.expires = options.expires.toUTCString(); + } + + let updatedCookie = encodeURIComponent(name) + "=" + encodeURIComponent(value); + + for (let optionKey in options) { + updatedCookie += "; " + optionKey; + let optionValue = options[optionKey]; + if (optionValue !== true) { + updatedCookie += "=" + optionValue; + } + } + + document.cookie = updatedCookie; +} + + +function deleteCookie(name) { + setCookie(name, "", { + 'max-age': -1 + }) +} diff --git a/6-data-storage/02-localstorage/1-form-autosave/solution.md b/6-data-storage/02-localstorage/1-form-autosave/solution.md new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/6-data-storage/02-localstorage/1-form-autosave/solution.md @@ -0,0 +1 @@ + diff --git a/6-data-storage/02-localstorage/1-form-autosave/solution.view/index.html b/6-data-storage/02-localstorage/1-form-autosave/solution.view/index.html new file mode 100644 index 0000000000..7be1989270 --- /dev/null +++ b/6-data-storage/02-localstorage/1-form-autosave/solution.view/index.html @@ -0,0 +1,10 @@ +<!doctype html> +<textarea style="width:200px; height: 60px;" id="area" placeholder="Write here"></textarea> +<br> +<button onclick="localStorage.removeItem('area');area.value=''">Clear</button> +<script> + area.value = localStorage.getItem('area'); + area.oninput = () => { + localStorage.setItem('area', area.value) + }; +</script> diff --git a/6-data-storage/02-localstorage/1-form-autosave/source.view/index.html b/6-data-storage/02-localstorage/1-form-autosave/source.view/index.html new file mode 100644 index 0000000000..95a741758d --- /dev/null +++ b/6-data-storage/02-localstorage/1-form-autosave/source.view/index.html @@ -0,0 +1,2 @@ +<!doctype html> +<textarea style="width:200px; height: 60px;" id="area"></textarea> diff --git a/6-data-storage/02-localstorage/1-form-autosave/task.md b/6-data-storage/02-localstorage/1-form-autosave/task.md new file mode 100644 index 0000000000..09f2c21059 --- /dev/null +++ b/6-data-storage/02-localstorage/1-form-autosave/task.md @@ -0,0 +1,10 @@ + +# フォームフィールドを自動保存する + +変更する毎に値を "自動保存" する `textarea` フィールドを作成してください。 + +これにより、利用者がページを閉じ、再度開いたときには、未完了の入力が表示されます。 + +このようになります: + +[iframe src="solution" height=120] diff --git a/6-data-storage/02-localstorage/article.md b/6-data-storage/02-localstorage/article.md new file mode 100644 index 0000000000..90f5ee602f --- /dev/null +++ b/6-data-storage/02-localstorage/article.md @@ -0,0 +1,246 @@ +# LocalStorage, sessionStorage + +Webストレージオブジェクト `localStorage` と `sessionStorage` では、キーバリューのペアをブラウザに保持することができます。 + +それらの興味深い点は、そのデータはページをリフレッシュしたり(`sessionStorage` の場合)、完全にブラウザを再起動しても(`localStorage`)生きていることです。後ほどすぐに見ていきます。 + +私たちは、すでに Cookie を持っています。なぜ追加のオブジェクトが必要なのでしょう? + +- Cookie とは違い、Web ストレージオブジェクトはリクエスト毎にサーバへは送信されません。そのため、より多くのものを格納することができます。ほとんどのブラウザは少なくとも2MB以上のデータを許容し、その設定を行う方法を持っています。 +- サーバは HTTP ヘッダ経由でストレージオブジェクトを操作することはできません。JavaScript 内ですべて行われます。 +- ストレージはそのオリジン (domain/protocol/portのセット)でバインドされます。つまり、異なるプロトコルやサブドメインは異なるストレージオブジェクトになり、お互いのデータにアクセスすることはできません。 + +どちらのストレージオブジェクトも同じメソッドとプロパティを提供しています: + +- `setItem(key, value)` -- key/value ペアを格納する. +- `getItem(key)` -- key を元に value を取得する +- `removeItem(key)` -- 指定された key (valueも) を削除する +- `clear()` -- すべてを削除する +- `key(index)` -- 指定位置の key を取得する +- `length` -- 格納されたアイテムの長さ + +どのように機能するのか見てみましょう。 + +## localStorage のデモ + +`localStorage` の主な特徴は: + +- 同じオリジンからのすべてのタブとウィンドウ間で共有されます。 +- データは期限切れになりません。ブラウザの再起動やOSの再起動後も残っています。 + +例えば、このコードを実行した場合... + +```js run +localStorage.setItem('test', 1); +``` + +...その後、ブラウザを閉じて/開く、あるいは別ウィンドウで同じページを開いた後、次のようにしてその値を取得することができます: + +```js run +alert( localStorage.getItem('test') ); // 1 +``` + +同じドメイン/ポート/プロトコル上にいるだけでよく、URLパスは異なっていてもかまいません。 + +`localStorage` は共有されているので、一方のウィンドウでデータを設定すると、もう一方のウィンドウでその変更を確認できます。 + +## オブジェクトライクなアクセス + +平易なオブジェクトを使ってキーの取得/設定をすることもできます。次のようになります: + +```js run +// キーをセット +localStorage.test = 2; + +// キーを取得 +alert( localStorage.test ); // 2 + +// キーの削除 +delete localStorage.test; +``` + +これらは歴史的な理由から許可されており、ほぼほぼ動作しますが、一般的には2つの理由から推奨されていません: + +1. キーがユーザが生成したものである場合、`length` や `toString`、あるいは `localStorage` の別の組み込みメソッドのように、なんにでもなる可能性があります。この場合、`getItem/setItem` は問題なく動作しますが、オブジェクトライクなアクセスは失敗します: + ```js run + let key = 'length'; + localStorage[key] = 5; // Error, can't assign length + ``` + +2. `storage` イベントがあり、これはデータが変更されたときにトリガーされます。このイベントはオブジェクトライクなアクセスの場合には発生しません。これについては、チャプターの後半で見ていきます。 + +## キーをループする + +メソッドは get/set/remove の機能を提供します。しかし、すべてのキーを取得する方法はどうやるのでしょう? + +残念ながら、ストレージオブジェクトは反復可能ではありません。 + +1つの方法は "配列ライク" なイテレーションを使うことです。: + +```js run +for(let i=0; i<localStorage.length; i++) { + let key = localStorage.key(i); + alert(`${key}: ${localStorage.getItem(key)}`); +} +``` + +もう1つは、オブジェクト固有の `for key in localStorage` ループを使用する方法です。 + +これはキーを繰り返し処理しますが、必要のない組み込みフィールドもいくつか出力します。: + +```js run +// bad try +for(let key in localStorage) { + alert(key); // getItem, setItem や他の組み込みのものを表示します +} +``` + +...なので、`hasOwnProperty` チェックでプロトタイプからのフィールドをフィルタする必要があります: + +```js run +for(let key in localStorage) { + if (!localStorage.hasOwnProperty(key)) { + continue; // "setItem", "getItem" etc のキーをスキップ + } + alert(`${key}: ${localStorage.getItem(key)}`); +} +``` + +...あるいは、`Object.keys` で "自身の" キーを取得し、必要に応じてそれらをループします: + +```js run +let keys = Object.keys(localStorage); +for(let key of keys) { + alert(`${key}: ${localStorage.getItem(key)}`); +} +``` + +`Object.keys`はプロトタイプを無視してオブジェクトに属するキーのみを返すので、後者はうまくいきます。 + +## 文字列のみ + +キーと値は両方とも文字列でなければならないことに注意してください。 + +数値やオブジェクトのような別の型の場合は、自動的に文字列に変換されます。 + +```js run +sessionStorage.user = {name: "John"}; +alert(sessionStorage.user); // [object Object] +``` + +オブジェクトを格納するには、`JSON` が使えます。: + +```js run +sessionStorage.user = JSON.stringify({name: "John"}); + +// sometime later +let user = JSON.parse( sessionStorage.user ); +alert( user.name ); // John +``` + +また、例えばデバッグ目的で、ストレージオブジェクト全体を文字列化することも可能です。: + +```js run +// オブジェクトを見やすくするために JSON.stringify にフォーマットオプションを追加 +alert( JSON.stringify(localStorage, null, 2) ); +``` + + +## sessionStorage + +`sessionStorage` オブジェクトは、 `localStorage` よりも使われることがずっと少ないです。 + +プロパティとメソッドは同じですが、より多くの制限があります: + +- `sessionStorage` は現在のブラウザタブ内でのみ存在します。 + - 同じページを持つ別タブは、異なるストレージを持ちます。 + - しかし、タブ内の iframe 間では共有されます(それらが同じオリジンから来ている想定)。 +- データはページ更新後も有効ですが、タブを閉じたり開いたりした場合は無効になります。 + +実際にそれを見てみましょう。 + +このコードを実行してください... + +```js run +sessionStorage.setItem('test', 1); +``` + +...次に、ページを更新してください。いまはまだデータを取得することができます。: + +```js run +alert( sessionStorage.getItem('test') ); // リフレッシュ後: 1 +``` + +...しかし、別のタブで同じページを開き、そこでもう一度試すと、上のコードは `null`、"見つからなかった" を返します。 + +それはまさに `sessionStorage` がオリジンだけでなく、ブラウザのタブにも結び付けられているからです。そのため、`sessionStorage` は控えめに使用されています。 + +## ストレージイベント + +データが、`localStorage` あるいは `sessionStorage` で更新されると、次のプロパティを持つ、[storage](https://www.w3.org/TR/webstorage/#the-storage-event) イベントがトリガーされます。: + +- `key` – 変更されたキー(`.clear()` が呼ばれた場合は null) +- `oldValue` – 古い値(キーが新しく追加された場合は、null) +- `newValue` – 新しい値(キーが削除された場合は null) +- `url` – 更新が行われた document の URL +- `storageArea` – 更新が行われた場所の `localStorage` あるいは `sessionStorage` オブジェクト + +重要なことは、それが起きた `window` を除く、ストレージがアクセス可能なすべての `window` オブジェクト上でイベントがトリガーされるということです。 + +詳しく説明しましょう。 + +想像してみてください、それぞれに同じサイトを持つ2つのウィンドウがあるとします。`localStorage` はそれらの間で共有されます。 + +```online +以下のコードをテストするには、このページを2つのブラウザウィンドウで開いてください。 +``` + +両方のウィンドウが `window.onstorage` をリッスンしている場合、各ウィンドウは他のウィンドウで行われた更新に反応します。 + +```js run +// 他の document から同じストレージに対して行われた更新をトリガーする +window.onstorage = event => { + if (event.key != 'now') return; + alert(event.key + ':' + event.newValue + " at " + event.url); +}; + +localStorage.setItem('now', Date.now()); +``` + +イベントには次のものが含まれていることにも留意してください。: `event.url`、これはデータが更新された document の URLです。 + +また、`event.storageArea` はストレージオブジェクトを含みます。イベントは `sessionStorage` と `localStorage` 両方で同じなので、`storageArea` は変更されたオブジェクトを参照します。変更に "応答" するために、その中に何かを戻すことができます。 + +**これは、同じオリジンからの異なるウィンドウがメッセージを交換することを可能にします。** + +モダンブラウザは [Broadcast channel API](https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API) もサポートしています。これは同一オリジン内のウィンドウ間のコミュニケーションのための特別なAPIです。これはより多くのことが考慮されていますが、あまりサポートされていません。`localStorage` に基づいて、そのAPIをどこでも利用できるようにするライブラリ(polyfill)があります。 + +## サマリ + +Web ストレージオブジェクト `localStorage` と `sessionStorage` はブラウザ内にキー/バリューを格納することができます。 +- `key` と `value` は両方文字列でなければなりません。 +- ブラウザによりますが、制限は 2MB+ です。 +- 有効期限はありません。 +- データはオリジン(domain/port/protocol)にバインドされています。 + +| `localStorage` | `sessionStorage` | +|----------------|------------------| +| 同一オリジンのすべてのタブとウィンドウ間で共有 | ブラウザタブ内で見えます。同一オリジンからの iframe も含みます | +| ブラウザの再起動でも残ります | タブを閉じると消えます | + +API: + +- `setItem(key, value)` -- key/value ペアを格納します +- `getItem(key)` -- key で値を取得します +- `removeItem(key)` -- 値と一緒にキーを削除します +- `clear()` -- すべてを削除します +- `key(index)` -- 指定位置の key を取得します +- `length` -- 格納されたアイテムの数 +- すべてのキーを取得するには `Object.keys` を使います +- オブジェクトプロパティとしてキーを使うことが可能ですが、この場合、`storage` イベントはトリガーされません。 + +ストレージイベント: + +- `setItem`, `removeItem`, `clear` の呼び出しでトリガーします。 +- 操作や document の `url`、ストレージオブジェクトに関するすべてのデータを含みます。 +- ストレージにアクセスできるすべての `window` オブジェクト(それを生成したものを除く)でトリガーします(`sessionStorage` の場合はタブ内、`localStorage` の場合は全体)。 diff --git a/6-data-storage/02-localstorage/sessionstorage.view/iframe.html b/6-data-storage/02-localstorage/sessionstorage.view/iframe.html new file mode 100644 index 0000000000..2555160b57 --- /dev/null +++ b/6-data-storage/02-localstorage/sessionstorage.view/iframe.html @@ -0,0 +1,9 @@ +<!doctype html> +<script> + window.addEventListener('storage', event => { + alert("iframe.html: onstorage"); + }); +</script> +<button onclick="sessionStorage.setItem('now', new Date())">sessionStorage.setItem</button> +</body> +</html> diff --git a/6-data-storage/02-localstorage/sessionstorage.view/index.html b/6-data-storage/02-localstorage/sessionstorage.view/index.html new file mode 100644 index 0000000000..110143e586 --- /dev/null +++ b/6-data-storage/02-localstorage/sessionstorage.view/index.html @@ -0,0 +1,10 @@ +<!doctype html> +<script> + window.addEventListener('storage', event => { + alert("index.html: onstorage"); + }); +</script> +<button onclick="sessionStorage.setItem('now', new Date())">sessionStorage.setItem</button> +<iframe src="iframe.html" style="height:100px"></iframe> +</body> +</html> diff --git a/6-data-storage/03-indexeddb/article.md b/6-data-storage/03-indexeddb/article.md new file mode 100644 index 0000000000..93fbdd3743 --- /dev/null +++ b/6-data-storage/03-indexeddb/article.md @@ -0,0 +1,744 @@ +libs: + - 'https://cdn.jsdelivr.net/npm/idb@3.0.2/build/idb.min.js' + +--- + +# IndexedDB + +InexedDB は組み込みのデータベースで、`localStorage` よりも遥かに強力です。 + +- key/value ストレージ: 値は何でもよく、複数のキーの型があります。 +- 信頼性のためのトランザクションをサポートします。 +- キー範囲のクエリ、インデックスをサポートします。 +- `localStorage` よりもずっと多くのデータを格納することができます。 + +通常、この機能は伝統的なクライアント-サーバアプリケーションには過大です。IndexedDB は、ServiceWorkers や他のテクノロジーと組み合わせるオフラインアプリケーションを想定しています。 + +[仕様](https://www.w3.org/TR/IndexedDB) に記載されている IndexedDB のネイティブインターフェースは、イベントベースです。 + +[idb](https://github.com/jakearchibald/idb) のように、promise ベースのラッパーを使って `async/await` を使うこともできます。これは非常に便利ですが、ラッパーは完璧ではありません。すべてのケースのイベントを置き換えることはできないので、イベントから始めて、その後ラッパーを使用しましょう。 + +## データベースを開く + +IndexedDB を使い始めるには、データベースを open します。 + +構文: + +```js +let openRequest = indexedDB.open(name, version); +``` + +- `name` -- 文字列。データベースの名前です。 +- `version` -- 正の整数で表現されるバージョン。デフォルトは `1` (後述). + +私たちは、異なる名前で多くのデータベースを持つことができ、それらはすべて現在のオリジン (domain/protocol/port) の中にあります。そのため、別のWebサイトは互いのデータベースにアクセスすることはできません。 + +呼び出し後、`openRequest` オブジェクトのイベントをリッスンする必要があります。: +- `success`: データベースの準備ができました。以降の処理ではデータベースオブジェクト `openRequest.result` を使います。 +- `error`: 開くのに失敗しました。 +- `upgradeneeded`: データベースのバージョンが古くなっています(下を見てください)。 + +**IndexedDB には、サーバサイドのデータベースにはない、組み込みの "スキーマバージョニング" の仕組みがあります。** + +サーバサイドのデータベースとは異なり、IndexedDB はクライアントサイドでありデータは手元にはありません。しかし、新しいアプリを公開するとき、データベースの更新が必要なことがあります。 + +ローカルデータベースバージョンが `open` で指定されたものより小さい場合、特別なイベント `upgradeneeded` がトリガーされ、必要に応じてバージョンを比較し、データ構造を更新する事ができます。 + +このイベントはデータベースがまだ存在しなかった場合にも起こるので、初期化の実行をすることもできます。 + +例えば、最初にアプリを公開するときには、バージョン `1` で open し、`upgradeneeded` ハンドラで初期化を実行します。: + +```js +let openRequest = indexedDB.open("store", *!*1*/!*); + +openRequest.onupgradeneeded = function() { + // クライアントがデータベースを持っていない場合にトリガーされます + // ...初期化を行います... +}; + +openRequest.onerror = function() { + console.error("Error", openResult.error); +};” + +openRequest.onsuccess = function() { + let db = openRequest.result; + // db オブジェクトを仕様してデータベースを操作します +}; +``` + +次のバージョンをリリースした時: + +```js +let openRequest = indexedDB.open("store", *!*2*/!*); + +// 既存のデータベースのバージョンをチェックし、必要なら更新する: +openRequest.onupgradeneeded = function() { + let db = openRequest.result; + switch(db.version) { // 既存の (古い) db のバージョン + case 0: + // バージョン 0 は、クライアントがデータベースを持っていないことを意味します + // 初期化を行います + case 1: + // クライアントはバージョン 1 + // 最新版に更新します + } +}; +``` + +`openRequest.onsuccess` の後、データベースオブジェクトは `openRequest.result` にあります。以降の操作でこれを使っていきます。 + +データベースを削除するには: + +```js +let deleteRequest = indexedDB.deleteDatabase(name) +// deleteRequest.onsuccess/onerror で結果を追跡します +``` + + +## オブジェクトストア + +オブジェクトストアは IndexedDB の中心となる概念です。他のデータベースでは "テーブル" や "コレクション" と呼ばれているものです。これはデータが格納される場所です。データベースは複数のストアを持つことがあります。: 1つはユーザ用、もう1つは商品用、などです。 + +"オブジェクトストア" という名前ではありますが、プリミティブを格納することも可能です。 + +**複雑なオブジェクト含め、ほぼどんな値でも格納することができます。** + +IndexedDB は [standard serialization algorithm](https://www.w3.org/TR/html53/infrastructure.html#section-structuredserializeforstorage) を使用してオブジェクトを複製し格納します。これは `JSON.stringify` に似ていますが、より強力で遥かに多くのデータタイプを格納することができます。 + +格納できないオブジェクトの例は、循環参照を持つオブジェクトです。このようなオブジェクトはシリアライズ可能ではありません。`JSON.stringify` も失敗します。 + +**ストア内のすべての値には一意となる `key` が必要です。** + +キーは次のいずれかのタイプでなければなりません: number, date, string, binary, または array。これは一意なオブジェクト識別子で、キーを使って値の検索/削除/更新をすることができます。 + +![](indexeddb-structure.svg) + +`localStorage` と同様、ストアに値を追加するときにキーを指定できます。これはプリミティブ値を格納するのに適しています。 しかし、オブジェクトを格納するとき、IndexedDB はオブジェクトプロパティをキーとして設定することを可能にし、それはとても便利です。もしくは、キーを自動生成することもできます。 + +オブジェクトストアを作成する構文: +```js +db.createObjectStore(name[, keyOptions]); +``` + +操作は同期であり、`await` は必要ないことに留意してください。 + +- `name` はストア名です。e.g. 本用に `"books"` など +- `keyOptions` は2つのプロパティのうち1つを持つオプションのオブジェクトです。 + - `keyPath` -- IndexedDBがキーをして使用するオブジェクトプロパティのパスです。e.g. `id. + - `autoIncrement` -- `true` の場合、新しく格納されたオブジェクトのキーは、インクリメントされる数値として、自動的に生成されます。 + +何もオプションを指定しない場合は、あとでオブジェクトを格納するときに明示的にキーを指定する必要があります。 + +例えば、このオブジェクトストアはキーとして `id` プロパティを使用します。: +```js +db.createObjectStore('books', {keyPath: 'id'}); +``` + +**オブジェクトストアは `upgradeneeded` ハンドラ内で DB バージョンを更新している間にだけ、生成/変更することができます。** + +これは技術的な制限によるものです。ハンドラの外側ではデータの追加/削除/更新が可能ですが、オブジェクトストアの変更はバージョンの更新中だけです。 + +アップグレードする方法は、主に2つあります: +1. バージョンを比較し、バージョンごとの操作を行います。 +2. あるいは、`db.objectStoreNames` で既存のオブジェクトストアの一覧が取得できます。このオブジェクトは [DOMStringList](https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#domstringlist) であり、存在チェックのためのメソッド `contains(name)` を提供します。そして存在するものに応じて更新を行います。 + +これは2つ目のアプローチの場合のデモです: + +```js +let openRequest = indexedDB.open("db", 1); + +// 存在しない場合には books のためのオブジェクトストアを作成する +openRequest.onupgradeneeded = function() { + let db = openRequest.result; + if (!db.objectStoreNames.contains('books')) { + db.createObjectStore('books', {keyPath: 'id'}); + } +}; +``` + +オブジェクトストアを削除するには: + +```js +db.deleteObjectStore('books') +``` + +## トランザクション + +"トランザクション" という用語は一般的で、多くのデータベースで使われています。 + +トランザクションはグループ操作であり、すべて成功したか/すべて失敗したかのいずれかになります。 + +例えば、ある人が何かを購入するとき、次のことが必要です。: +1. 口座からお金を引き落とします。 +2. 購入者の持ち物に購入した商品を追加します。 + +もしも最初の処理が完了し、その後、例えば停電などで上手く処理できず次の処理が失敗すると、非常にまずいでしょう。どちらも成功する(購入完了)もしくは失敗する(少なくとも購入者はお金は引かれておらず、リトライできる)べきです。 + +トランザクションはそれを保証します。 + +**IndexedDB でのすべてのデータ操作はトランザクション内で行わなければなりません。** + +トランザクションを開始するには: + +```js +db.transaction(store[, type]); +``` + +- `store` はトランザクションがアクセスするストア名です。e.g. `"books"`。複数のストアにアクセスする場合は、ストア名の配列を指定します。 +- `type` はトランザクションのタイプです。以下のいずれかです: + - `readonly`: 参照のみ。デフォルトです。 + - `readwrite`: 読み書き可能ですが、オブジェクトストアの変更はできません。 + +`versionchange` というトランザクションタイプもあります。: このようなトランザクションは何でもできますが、手動で作ることはできません。IndexedDBは、`updateneeded` ハンドラの場合、データベースを開くときに `versionchange` トランザクションを自動的に作成します。そのため、ここがデータベース構造の更新やオブジェクトストアの作成/削除が可能な唯一の場所になります。 + +```smart header="トランザクションタイプとは何のためのあるのでしょう?" +トランザクションが `readonly` か `readwrite` のいずれかにラベル付けされる必要があるのは、パフォーマンスが理由です。 + +多くの `readonly` トランザクションは同じストアに同時にアクセス可能ですが、`readwrite` トランザクションはできません。`readwrite` トランザクションは書き込みのためにストアを "ロック" します。次のトランザクションは、同じストアにアクセスする前にまえのトランザクションが終了するまで待たなければなりません。 +``` + +トランザクションが作成されたら、次のようにしてストアにアイテムを追加することができます: + +```js +let transaction = db.transaction("books", "readwrite"); // (1) + +// 操作するためにオブジェクトストアを取得 +*!* +let books = transaction.objectStore("books"); // (2) +*/!* + +let book = { + id: 'js', + price: 10, + created: new Date() +}; + +*!* +let request = books.add(book); // (3) +*/!* + +request.onsuccess = function() { // (4) + console.log("Book added to the store", request.result); +}; + +request.onerror = function() { + console.log("Error", request.error); +}; +``` + +基本的に4つのステップがあります。: + +1. トランザクションを作成し、`(1)` でアクセスしようとしているすべてのストアについて言及します。 +2. `(2)` で `transaction.objectStore(name)` を使ってストアオブジェクトを取得します。 +3. `(3)` でオブジェクトストアにリクエストを実行します: `books.add(book)`。 +4. ...`(4)` でリクエストの成功/エラー を処理し、必要に応じて他のリクエストをする、など。 + +オブジェクトストアは値を格納するための2つのメソッドをサポートしています。: + +- **put(value, [key])** + ストアに `value` を追加します。`key` は、オブジェクトストアが `keyPath` や `autoIncrement` オプションを持っていなかった場合にのみ提供されます。もし同じキーをもつ値がすでに存在している場合には、値は置き換えられます。 + +- **add(value, [key])** + `put` と同じですが、同じキーを持つ値がすでに存在する場合、リクエストは失敗し、`"ConstraintError"` という名前のエラーが生成されます。 + +データベースを開くときと同じように、リクエストを送信(`books.add(book)`)し、`success/error` イベントをまちます。 + +- `add` の場合の `request.result` は新しいオブジェクトのキーです。 +- エラーは `request.error` にあります(あれば)。 + +## トランザクションの自動コミット + +上の例では、トランザクションを開始して、`add` リクエストを行いましたが、前に述べたように、トランザクションには複数のリクエストを関連付けることも可能です。そしてそれらはすべて成功か失敗かのどちらかでないといけません。トランザクションを終了としてマークする(これ以上リクエストがない)にはどのようにしたらよいでしょうか。 + +一言で言うと: そのようなことはしません。 + +仕様の次のバージョン 3.0 では、おそらくトランザクションを手動で終了させる方法があるでしょうが、今のところ、2.0 にはありません。 + +**すべてのトランザクションの要求が終了し、[microtasks queue](info:microtask-queue) が空になると、自動的にコミットされます。** + +通常、トランザクションはすべてのリクエストが完了し、現在のコードが終了したときにコミットすると想定できます。 + +なので、上の例ではトランザクションを終了させるための特別な呼び出しは必要ありません。 + +トランザクション自動コミットの原則には重要な副作用があります。トランザクションの途中で `fetch`, `setTimeout` といった非同期操作を挿入することができません。IndexedDB はそれらが終わるまでトランザクションを待機させません。 + +以下のコードでは、行 `(*)` の `request2` は失敗します。トランザクションはすでにコミットされており、ここではどんなリクエストも行うことができないためです: + +```js +let request1 = books.add(book); + +request1.onsuccess = function() { + fetch('/').then(response => { +*!* + let request2 = books.add(anotherBook); // (*) +*/!* + request2.onerror = function() { + console.log(request2.error.name); // TransactionInactiveError + }; + }); +}; +``` + +これは `fetch` が非同期操作、macrotask であるためです。トランザクションはブラウザが macrotask の実行を開始する前にクローズされます。 + +IndexedDB 仕様の作成者は、トランザクションは短命であるべきだと考えています。主にパフォーマンス上の理由からです。 + +特に、`readwrite` トランザクションは書き込みのためにストアを "ロック" します。したがって、アプリケーションの一部が `books` オブジェクトストア上で `readwrite` を開始した場合、同じことがしたかったアプリケーションの別の部分は待機しなければなりません。新たなトランザクションは、最初のトランザクションが終了するまで "ハング" します。 トランザクションに時間がかかると、奇妙な遅延につながる可能性があります。 + +では何をすればよいでしょうか? + +上の例では、新たなリクエスト `(*)` の直前に新しい `db.transaction` を作成することができます。 + +ですが、1つのトランザクション内で操作をまとめたい場合には、IndexedDB トランザクション部分と "その他" の非同期部分に分割するのがさらに良い方法でしょう。 + +まず、`fetch` をして必要に応じてデータを準備します。その後、トランザクションを作成しすべてのデータベースリクエストを実行すると、うまく機能します。 + +正常に完了した瞬間を検知するには、`transaction.oncomplete` イベントをリッスンします: + +```js +let transaction = db.transaction("books", "readwrite"); + +// ...操作を実行します... + +transaction.oncomplete = function() { + console.log("Transaction is complete"); +}; +``` + +`complete` だけがトランザクション全体が保存されたことを保証します。個々のリクエストは成功したかもしれませんが、最終的な書き込み操作は失敗する可能性があります(例. I/O エラーなど) + +トランザクションを手動で停止するには、以下を呼び出します: + +```js +transaction.abort(); +``` + +これにより、その中のリクエストにより行われたすべての変更をキャンセルし、`transaction.onabort` イベントをトリガーします。 + +## エラーハンドリング + +書き込みリクエストは失敗する可能性があります。 + +これは、われわれ側で発生しうるエラーだけでなく、トランザクション自体とは関連しない理由から発生することも予想されます。例えば、ストレージ容量を超えた場合です。そのため、このようなケースを処理する準備ができている必要があります。 + +**リクエストが失敗すると、トランザクションは自動的に中止され、すべての変更がキャンセルされます。** + +ケースによっては、既存の変更をキャンセルせずに失敗を処理(例えば別のリクエストを試みる)し、トランザクションを継続したいことがあります。これは可能です。`request.onerror` ハンドラでは、`event.preventDefault()` 呼び出しをすることで、トランザクションを中止しないようにすることができます。 + +以下の例は、すでに存在するキーと同じキー(`id`)で新しい本が追加されています。この場合、`store.add` メソッドは `"ConstraintError"` を生成します。この例ではトランザクションをキャンセルせずに処理しています。: + +```js +let transaction = db.transaction("books", "readwrite"); + +let book = { id: 'js', price: 10 }; + +let request = transaction.objectStore("books").add(book); + +request.onerror = function(event) { + // 同じ id のオブジェクトが既に存在する場合、ConstraintError が発生します + if (request.error.name == "ConstraintError") { + console.log("Book with such id already exists"); // エラー処理 + event.preventDefault(); // トランザクションを中止しません + // 別のキーを利用する?など + } else { + // unexpected error + // 処理できないので、トランザクションは中止します + } +}; + +transaction.onabort = function() { + console.log("Error", transaction.error); +}; +``` + +### イベント委譲(delegation) + +すべてのリクエストに対して onerror/onsuccess が必要でしょうか?毎回ではありません。ので、代わりにイベント委譲が利用できます。 + +**IndexedDB のイベントバブル: `request` -> `transaction` -> `database`.** + +すべてのイベントは キャプチャリングとバブリングを持つ DOM イベントで、通常はバブリングステージだけが利用されます。 + +したがって、レポートや他の目的のために `db.onerror` ハンドラを使用してすべてのエラーをキャッチすることが可能です。 + + +```js +db.onerror = function(event) { + let request = event.target; // エラーが発生したリクエスト + + console.log("Error", request.error); +}; +``` + +...ですが、仮にエラーが完全に処理されたら?この場合はレポートしたくはありません。 +`request.onerror` で `event.stopPropagation()` を利用することでバブリング、つまり `db.onerror` を停止することができます。 + +```js +request.onerror = function(event) { + if (request.error.name == "ConstraintError") { + console.log("Book with such id already exists"); // エラー処理 + event.preventDefault(); // トランザクションを中止したくない + event.stopPropagation(); // エラーをバブルしません、よく考えてください + } else { + // 何もしません + // トランザクションは中止されます + // transaction.onabort でエラーを扱うことができます + } +}; +``` + +## キーで検索する + +オブジェクトストアの検索には主に2つの種類があります。: +1. キー or キー範囲によるもの。つまり、"books" ストレージでは `book.id` です。 +2. 別のオブジェクトフィールドによるもの。例えば、`book.price`。 + +最初に、キーとキー範囲 `(1)` を取り扱いましょう。 + +検索を伴うメソッドは、正確なキー あるいはいわゆる "範囲クエリ"("キー範囲" を指定する [IDBKeyRange](https://www.w3.org/TR/IndexedDB/#keyrange)オブジェクト) のいずれかをサポートします。 + +範囲は次の呼び出しを使用して生成されます: + +- `IDBKeyRange.lowerBound(lower, [open])` 意味: `≥lower` (`open` が true なら `>lower`) +- `IDBKeyRange.upperBound(upper, [open])` 意味: `≤upper` (`open` が true なら `<upper`) +- `IDBKeyRange.bound(lower, upper, [lowerOpen], [upperOpen])` 意味: `lower` と `upper` の間. open フラグが true の場合、対応するキーは範囲に含まれません。 +- `IDBKeyRange.only(key)` -- 単一の `key` のみで構成される範囲で、めったに使われません。 + +すべての検索メソッドは正確なキーまたはキー範囲のいずれかの `query` 引数を受け付けます。: + +- `store.get(query)` -- キー or 範囲で、最初の値を検索します。 +- `store.getAll([query], [count])` -- すべての値を検索します。`count` が指定されている場合はその数で制限されます。 +- `store.getKey(query)` -- クエリを満たす最初のキーを検索します。通常は範囲です。 +- `store.getAllKeys([query], [count])` -- クエリを満たすすべてのキーを検索します。通常は範囲で、`count` が指定されている場合はその数までです。 +- `store.count([query])` -- クエリを満たすキーの総数を取得しまs.通常は範囲です。 + +例えば、ストアに大量の本(books)があるとします。`id` フィールドはキーなので、これらすべてのメソッドは `id` で検索ができること、忘れないでください。 + +リクエスト例: + +```js +// 単一の本を取得 +books.get('js') + +// 'css' <= id <= 'html' の本を取得 +books.getAll(IDBKeyRange.bound('css', 'html')) + +// id < 'html' の本を取得 +books.getAll(IDBKeyRange.upperBound('html', true)) + +// すべての本を取得 +books.getAll() + +// id > 'js' のすべてのキーを取得 +books.getAllKeys(IDBKeyRange.lowerBound('js', true)) +``` + +```smart header="オブジェクトストアは常にソートされています。" +オブジェクトストアは内部的に、キーにより値をソートしています。 + +そのため、多くの値を返すリクエストは、常にキー順にソートされた結果を返します。 +``` + + +## index 付きの任意のフィールドで検索する + +他のオブジェクトフィールドで検索するには、"index" と呼ばれる追加のデータ構造を生成する必要があります。 + +index は特定のオブジェクトフィールドを追跡するストアへの "アドオン" です。そのフィールドの値ごとに、その値を持つオブジェクトのキーのリストを格納します。以下により詳細な図があります。 + +構文: + +```js +objectStore.createIndex(name, keyPath, [options]); +``` + +- **`name`** -- index 名, +- **`keyPath`** -- index が追跡すべきオブジェクトフィールドのパス(将来そのフィールドで検索します) +- **`option`** -- 次のプロパティをもつオプションのオブジェクト: + - **`unique`** -- true の場合、ストアには `keyPath` で指定された値をもつオブジェクトが1つしかないことを示します。重複を追加しようとした場合、index はエラーを生成することでそれを強制します。 + - **`multiEntry`** -- `keyPath` の値が配列の場合にのみ使われます。この場合、デフォルトでは index は配列全体をキーとして扱いますが、`multiEntry` が true の場合は、index は配列内の各値のストアオブジェクトのリストを維持します。したがって、配列要素は index キーになります。 + +われわれの例では、`id` でキー設定された本を格納しています。 + +ここで、`price` で検索したいとしましょう。 + +まず、index を作成する必要があります。オブジェクトストア同様、`upgradeneeded` で行わなければなりません。: + +```js +openRequest.onupgradeneeded = function() { + // index はここ、バージョン変更のトランザクションの中で作成する必要があります + let books = db.createObjectStore('books', {keyPath: 'id'}); +*!* + let index = books.createIndex('price_idx', 'price'); +*/!* +}; +``` + +- index は `price` フィールドを追跡します。 +- price(価格)はユニークではないので、同じ価格で複数の本が存在する可能性があります。そのため、`unique` オプションは設定しません。 +- price(価格)は配列ではないので、`multiEntry` フラグは適用されません。 + +`inventory` に4冊の本があるとします。これは `index` が何であるかを正確に示す図です: + +![](indexeddb-index.svg) + +既に述べた通り、`price` (2つ目の引数)の各値の index は、その 価格 をもつキーの一覧を保持します。 + +index は自動で最新状態が維持されるので、気にする必要は有りません。 + +いま、特定の価格で検索がしたい場合、単に index に対して同じ検索メソッドを適用するだけです。: + +```js +let transaction = db.transaction("books"); // readonly +let books = transaction.objectStore("books"); +let priceIndex = books.index("price_idx"); + +*!* +let request = priceIndex.getAll(10); +*/!* + +request.onsuccess = function() { + if (request.result !== undefined) { + console.log("Books", request.result); // price=10 の本の配列 + } else { + console.log("No such books"); + } +}; +``` + +`IDBKeyRange` で範囲を作成し、安い/高い本を探すこともできます: + +```js +// price <= 5 の本を見つける +let request = priceIndex.getAll(IDBKeyRange.upperBound(5)); +``` + +index は内部的には追跡されているオブジェクトフィールド(このケースでは `price`)でソートされています。なので、検索するとき、結果もまた `price` でソートされています。 + +## ストアから削除する + +`delete` メソッドはクエリによって削除する値を調べます。呼び出し形式は `getAll` と同じです: + +- **`delete(query)`** -- クエリにマッチする値を削除します + +例: +```js +// id='js' の本を削除します +books.delete('js'); +``` + +価格 あるいは別のオブジェクトフィールドを元に本を削除したい場合は、最初に index でキーを見つけ、その後に `delete` を呼び出します。: + +```js +// price = 5 のキーを見つける +let request = priceIndex.getKey(5); + +request.onsuccess = function() { + let id = request.result; + let deleteRequest = books.delete(id); +}; +``` + +すべての削除するには: +```js +books.clear(); // ストレージをクリアします +``` + +## カーソル(Cursors) + +`getAll/getAllKeys` のようなメソッドは キー/値 の配列を返します。 + +ですが、オブジェクトストレージは巨大になり、利用可能なメモリよりも大きくなる可能性があります。`getAll` はすべてのレコードを配列として取得することはできないでしょう。 + +何をしたらよいでしょう? + +カーソルはそれを回避する手段を提供します。 + +***カーソル* は与えられたクエリでオブジェクトストレージを横断する特別なオブジェクトで、一度に1つのキー/値を返すため、メモリを節約します。** + +オブジェクトストアは内部的にはキーでソートされているので、カーソルはキー順(デフォルトでは昇順)でストアを移動します。 + +構文: +```js +// getAll と似ていますが カーソルに対してです: +let request = store.openCursor(query, [direction]); + +// 値ではなくキーを得るには(getAllKeysのような): store.openKeyCursor +``` + +- **`query`** はキーまたはキー範囲で、`getAll` と同じです。 +- **`direction`** はオプションの引数で、使用する順序です: + - `"next"` -- デフォルトで, カーソルは最も小さいキーのレコードから上に移動します。 + - `"prev"` -- 逆順です: 最も大きなキーを持つレコードから下に移動します。 + - `"nextunique"`, `"prevunique"` -- 上と同じですが、同じキーを持つレコードをスキップします(index 上のカーソルのみ。例: price=5 の複数の本の場合、最初の1冊だけが返却されます)。 + +**カーソルの主な違いは `request.onsuccess` が複数回トリガーされることです: 各結果に対し1度トリガーされます。** + +これは、カーソルの使用例です: + +```js +let transaction = db.transaction("books"); +let books = transaction.objectStore("books"); + +let request = books.openCursor(); + +// カーソルで見つかった各本に対して呼び出されます +request.onsuccess = function() { + let cursor = request.result; + if (cursor) { + let key = cursor.key; // book key (id フィールド) + let value = cursor.value; // book オブジェクト + console.log(key, value); + cursor.continue(); + } else { + console.log("No more books"); + } +}; +``` + +主なカーソルメソッドは以下です: + +- `advance(count)` -- カーソルを `count` 数進め、値をスキップします。 +- `continue([key])` -- マッチした範囲の次の値にカーソルを進めます(あるいは指定された場合は、その `key` の直後) + +カーソルに一致する値がもっとあるか否かは、`onsuccess` を呼び出した後 `result` を見ることで、次のレコードを指すカーソルあるいは `undefined` が取得できます。 + +上記の例では、オブジェクトストア用のカーソルが作成されました。 + +しかし、index 上にカーソルを作成することもできます。御存知の通り、index を利用することでオブジェクトフィールドで検索することができます。index 上のカーソルはオブジェクトストア上のカーソルとまったく同じように機能します、つまり、一度に1つの値を返すことでメモリを節約します。 + +index 上のカーソルの場合、`cursor.key` は index キー(例, price )であり、オブジェクトキーに対しては `cursor.primaryKey` プロパティを使用する必要があります: + +```js +let request = priceIdx.openCursor(IDBKeyRange.upperBound(5)); + +// called for each record +request.onsuccess = function() { + let cursor = request.result; + if (cursor) { + let primaryKey = cursor.primaryKey; // 次のオブジェクトストアキー(id フィールド) + let value = cursor.value; // 次のオブジェクトストアオブジェクト (book オブジェクト) + let key = cursor.key; // 次の index キー (price) + console.log(key, value); + cursor.continue(); + } else { + console.log("No more books"); + } +}; +``` + +## Promise ラッパー + +すべてのリクエストに `onsuccess/onerror` を追加するのはとても面倒な作業です。イベント委譲を使用することで、楽にできる場合があることがあります。例えば、トランザクション全体にハンドラを設定しますが、`async/await` ははるかに便利です。 + +このチャプターでは、薄いPromise ラッパー <https://github.com/jakearchibald/idb> を使ってみましょう。これは [promise 化](info:promisify) された IndexedDB メソッドを持つ、グローバルな `idb` オブジェクトを生成します。 + + +すると、`onsuccess/onerror` の代わりに、次のように記述することができます: + +```js +let db = await idb.openDB('store', 1, db => { + if (db.oldVersion == 0) { + // 初期化の実行 + db.createObjectStore('books', {keyPath: 'id'}); + } +}); + +let transaction = db.transaction('books', 'readwrite'); +let books = transaction.objectStore('books'); + +try { + await books.add(...); + await books.add(...); + + await transaction.complete; + + console.log('jsbook saved'); +} catch(err) { + console.log('error', err.message); +} + +``` + +"通常の async コード" と "try...catch" だけになります。 + +### エラーハンドリング + +エラーをキャッチしない場合、最も近い外側の `try...catch` までエラーがきます。 + +キャッチされなかったエラーは `window` オブジェクトの"未処理の prmise 拒否" イベントになります。 + +次のようにして、このようなエラーを処理することができます: + +```js +window.addEventListener('unhandledrejection', event => { + let request = event.target; // IndexedDB ネイティブのリクエストオブジェクト + let error = event.reason; // 未処理のエラーオブジェクト。request.error と同じ + ...report about the error... +}); +``` + +### "非アクティブなトランザクション" の落とし穴 + +すでにご存知のように、ブラウザが現在のコードと microtask を実行するとすぐにトランザクションは自動コミットされます。そのため、トランザクション中に `fetch` のような *macrotask* を置いた場合、トランザクションはその終了を待たず自動コミットします。したがって、次のリクエストは失敗するでしょう。 + +promise ラッパーや `async/await` の場合も同じです。 + +これはトランザクションの途中に `fetch` がある例です: + +```js +let transaction = db.transaction("inventory", "readwrite"); +let inventory = transaction.objectStore("inventory"); + +await inventory.add({ id: 'js', price: 10, created: new Date() }); + +await fetch(...); // (*) + +await inventory.add({ id: 'js', price: 10, created: new Date() }); // Error +``` + +`fetch` `(*)` の後にある次の `inventory.add` は "非アクティブなトランザクション" エラーで失敗します。その時点でトランザクションは既にコミットされクローズされているためです。 + +回避策はネイテイブのIndexedDB を使用する場合と同じです。新たなトランザクションを作るか、単に物事を分割するか、です。 +1. データを準備し、最初に必要なものをすべて取得します。 +2. 次に、データベースに保存します。 + +### ネイティブオブジェクトを取得する + +内部的には、ラッパーは `onerror/onsuccess` が追加されたネイテイブの IndexedDB リクエストを実行し、その結果を reject/resolve する promise を返します。 + +ほとんどの場合、これで問題なく動作します。例はライブラリのページ <https://github.com/jakearchibald/idb> にあります。 + +レアケースですが、オリジナルの `request` オブジェクトが必要なときは、promise の `promise.request` プロパティでアクセスすることができます: + +```js +let promise = books.add(book); // promise を取得します (await は不要) + +let request = promise.request; // ネイティブのリクエストオブジェクト +let transaction = request.transaction; // ネイティブのトランザクションオブジェクト + +// ...do some native IndexedDB voodoo... + +let result = await promise; // 必要であれば +``` + +## サマリ + +IndexedDB はシンプルな key-value データベースであり、オフラインアプリケーションには十分強力なものでありつつ、使いやすいものです。 + +最良のマニュアルは仕様です。[現在のもの](https://www.w3.org/TR/IndexedDB-2/) は 2.0 ですが、[3.0](https://w3c.github.io/IndexedDB/) のいくつかのメソッド(大きな違いはありません)は部分的にサポートされています。 + +基本的な使用方法は次のフェーズで説明できます: + +1. [idb](https://github.com/jakearchibald/idb) のような promise ラッパーを取得します。 +2. データベースをオープンします `idb.openDb(name, version, onupgradeneeded)` +3. リクエストの場合: + - トランザクションを作成します `db.transaction('books')` (必要に応じて読み書き)。 + - オブジェクトストアを取得します `transaction.objectStore('books')`。 +4. 次に、キーで検索するためにオブジェクトストアのメソッドを直接呼び出します。 + - オブジェクトフィールドで検索する場合には index を作成します。 +5. データがメモリに収まらない場合には、カーソルを使用します。 + +これは小さなデモアプリです: + +[codetabs src="books" current="index.html"] diff --git a/6-data-storage/03-indexeddb/books.view/index.html b/6-data-storage/03-indexeddb/books.view/index.html new file mode 100644 index 0000000000..11c12da7b9 --- /dev/null +++ b/6-data-storage/03-indexeddb/books.view/index.html @@ -0,0 +1,70 @@ +<!doctype html> +<script src="https://cdn.jsdelivr.net/npm/idb@3.0.2/build/idb.min.js"></script> + +<button onclick="addBook()">Add a book</button> +<button onclick="clearBooks()">Clear books</button> + +<p>Books list:</p> + +<ul id="listElem"></ul> + +<script> +let db; + +init(); + +async function init() { + db = await idb.openDb('booksDb', 1, db => { + db.createObjectStore('books', {keyPath: 'name'}); + }); + + list(); +} + +async function list() { + let tx = db.transaction('books'); + let bookStore = tx.objectStore('books'); + + let books = await bookStore.getAll(); + + if (books.length) { + listElem.innerHTML = books.map(book => `<li> + name: ${book.name}, price: ${book.price} + </li>`).join(''); + } else { + listElem.innerHTML = '<li>No books yet. Please add books.</li>' + } + + +} + +async function clearBooks() { + let tx = db.transaction('books', 'readwrite'); + await tx.objectStore('books').clear(); + await list(); +} + +async function addBook() { + let name = prompt("Book name?"); + let price = +prompt("Book price?"); + + let tx = db.transaction('books', 'readwrite'); + + try { + await tx.objectStore('books').add({name, price}); + await list(); + } catch(err) { + if (err.name == 'ConstraintError') { + alert("Such book exists already"); + await addBook(); + } else { + throw err; + } + } +} + +window.addEventListener('unhandledrejection', event => { + alert("Error: " + event.reason.message); +}); + +</script> diff --git a/6-data-storage/03-indexeddb/indexeddb-index.svg b/6-data-storage/03-indexeddb/indexeddb-index.svg new file mode 100644 index 0000000000..b7f34cadd3 --- /dev/null +++ b/6-data-storage/03-indexeddb/indexeddb-index.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="440" height="289" viewBox="0 0 440 289"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="data-storage" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="indexeddb-index.svg"><path id="Rectangle-1" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M124 43v58H17V43h107z"/><path id="Rectangle-1-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M130 43v58H17V43h113z"/><text id="id:-'html'-price:-3" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="27" y="66">id: 'html'</tspan> <tspan x="27" y="81">price: 3</tspan></text><path id="Rectangle-1-Copy-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M130 101v58H17v-58h113z"/><text id="id:-'css'-price:-5" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="27" y="124">id: 'css'</tspan> <tspan x="27" y="139">price: 5</tspan></text><text id="3:-['html']-5:-['css" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="16" font-weight="normal"><tspan x="245" y="57">3: ['html']</tspan> <tspan x="245" y="87">5: ['css']</tspan> <tspan x="245" y="117">10: ['js','nodejs']</tspan></text><path id="Rectangle-1-Copy-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M130 159v58H17v-58h113z"/><text id="id:-'js'-price:-10" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="27" y="182">id: 'js'</tspan> <tspan x="27" y="197">price: 10</tspan></text><path id="Rectangle-1-Copy-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M130 217v58H17v-58h113z"/><text id="id:-'nodejs'-price:" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="27" y="240">id: 'nodejs'</tspan> <tspan x="27" y="255">price: 10</tspan></text><text id="books" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="18" font-weight="normal"><tspan x="16" y="31">books</tspan></text><text id="index" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="18" font-weight="normal"><tspan x="245" y="31">index</tspan></text><path id="Line" fill="#C06334" fill-rule="nonzero" d="M223.5 49.5l14 7-14 7v-6h-3v-2h3v-6zm-7 6v2h-5v-2h5zm-9 0v2h-5v-2h5zm-9 0v2h-5v-2h5zm-9 0v2h-5v-2h5zm-9 0v2h-5v-2h5zm-9 0v2h-5v-2h5zm-9 0v2h-5v-2h5zm-9 0v2h-5v-2h5zm-9 0v2h-5v-2h5z"/><path id="Line-Copy" fill="#C06334" fill-rule="nonzero" d="M141.258 119.01l.677 1.882-.942.338-2.823 1.014-.941.338-.676-1.882.941-.338 2.823-1.014.941-.338zm8.47-3.043l.677 1.882-4.706 1.69-.676-1.882.941-.338 2.823-1.014.942-.338zm8.47-3.043l.677 1.883-4.706 1.69-.676-1.882 4.706-1.69zm8.47-3.042l.677 1.882-.941.338-3.765 1.352-.676-1.882.941-.338 3.765-1.352zm8.47-3.043l.677 1.882-4.706 1.69-.676-1.882 4.706-1.69zm8.47-3.043l.677 1.883-.941.338-3.765 1.352-.676-1.882 4.706-1.69zm8.47-3.042l.677 1.882-.941.338-3.765 1.352-.676-1.882.941-.338 2.824-1.014.94-.338zm8.471-3.043l.676 1.882-.941.338-2.823 1.015-.941.338-.677-1.883 3.765-1.352.941-.338zm8.47-3.043l.676 1.883-.94.338-2.824 1.014-.941.338-.676-1.882 3.764-1.352.941-.339zm12.939-11.023L237.5 85.5l-10.81 11.32-2.028-5.646-1.791.644-.941.338-.676-1.883.94-.338 1.792-.643-2.028-5.647zm-4.469 7.98l.676 1.883-.94.338-2.824 1.014-.941.338-.676-1.882.94-.338 2.824-1.014.941-.338z"/><path id="Line-Copy-2" fill="#C06334" fill-rule="nonzero" d="M144.508 164.175l.836 1.817-.908.418-2.726 1.254-.908.418-.836-1.817.908-.418 2.726-1.254.908-.418zm8.177-3.76l.836 1.816-.909.418-2.725 1.254-.909.418-.836-1.817.909-.418 2.725-1.254.909-.418zm8.176-3.762l.836 1.817-.908.418-2.726 1.254-.908.418-.836-1.817.908-.418 2.726-1.254.908-.418zm8.177-3.761l.836 1.817-.909.418-2.725 1.254-.909.417-.836-1.817.909-.417 2.725-1.254.909-.418zm8.176-3.761l.836 1.817-.909.418-2.725 1.253-.909.418-.835-1.817.908-.418 2.726-1.253.908-.418zm8.177-3.761l.835 1.817-.908.418-2.726 1.253-.908.418-.836-1.817.909-.418 2.725-1.253.909-.418zm8.176-3.762l.836 1.817-.909.418-2.725 1.254-.909.418-.836-1.817.909-.418 2.725-1.254.909-.418zm8.176-3.76l.836 1.816-.908.418-2.726 1.254-.908.418-.836-1.817.908-.418 2.726-1.254.908-.418zm8.177-3.762l.836 1.817-.909.418-2.725 1.254-.909.418-.836-1.817.909-.418 2.725-1.254.909-.418zm11.936-12.095l15.644.509-9.794 12.21-2.508-5.451-1.724.793-.908.418-.836-1.817.909-.417 1.723-.794-2.506-5.45zm-3.76 8.334l.836 1.817-.909.418-2.725 1.254-.908.418-.836-1.817.908-.418 2.726-1.254.908-.418z"/><path id="Line-Copy-3" fill="#C06334" fill-rule="nonzero" d="M143.315 221.096l1.377 1.45-.725.69-2.175 2.065-.725.69-1.377-1.45.725-.69 2.175-2.066.725-.689zm6.525-6.198l1.377 1.45-.725.688-2.175 2.067-.725.688-1.377-1.45.725-.688 2.175-2.067.725-.688zm6.525-6.199l1.377 1.45-.725.689-2.175 2.066-.725.689-1.377-1.45.725-.689 2.175-2.066.725-.689zm6.525-6.199l1.377 1.45-.725.689-2.175 2.066-.725.689-1.377-1.45.725-.689 2.175-2.066.725-.689zm6.525-6.198l1.377 1.45-.725.688-2.175 2.067-.725.688-1.377-1.45.725-.688 2.175-2.067.725-.688zm6.525-6.2l1.377 1.45-.725.69-2.175 2.066-.725.689-1.377-1.45.725-.69 2.175-2.065.725-.69zm6.525-6.198l1.377 1.45-.725.689-2.175 2.066-.725.689-1.377-1.45.725-.689 2.175-2.066.725-.689zm6.525-6.199l1.377 1.45-.725.689-2.175 2.066-.725.689-1.377-1.45.725-.689 2.175-2.066.725-.689zm6.525-6.198l1.377 1.45-.725.688-2.175 2.067-.725.688-1.377-1.45.725-.688 2.175-2.067.725-.688zm6.525-6.2l1.377 1.45-.725.69-2.175 2.066-.725.689-1.377-1.45.725-.69 2.175-2.065.725-.69zm6.525-6.198l1.377 1.45-.725.689-2.175 2.066-.725.689-1.377-1.45.725-.689 2.175-2.066.725-.689zm6.525-6.199l1.377 1.45-.725.689-2.175 2.066-.725.689-1.377-1.45.725-.689 2.175-2.066.725-.689zm6.525-6.198l1.377 1.45-.725.688-2.175 2.067-.725.688-1.377-1.45.725-.688 2.175-2.067.725-.688zM237.5 133l-5.329 14.717-4.133-4.35-1.42 1.35-.726.69-1.377-1.45.725-.69 1.42-1.35-4.131-4.35L237.5 133z"/></g></g></svg> \ No newline at end of file diff --git a/6-data-storage/03-indexeddb/indexeddb-structure.svg b/6-data-storage/03-indexeddb/indexeddb-structure.svg new file mode 100644 index 0000000000..a13e16918d --- /dev/null +++ b/6-data-storage/03-indexeddb/indexeddb-structure.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="484" height="267" viewBox="0 0 484 267"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="data-storage" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="indexeddb-structure.svg"><path id="Rectangle-1-Copy" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M14 59h113v31H14z"/><text id="key1:-value1" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="22" y="79">key1: value1</tspan></text><path id="Rectangle-1-Copy-2" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M14 90h113v31H14z"/><path id="Rectangle-1-Copy-4" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M14 121h113v31H14z"/><path id="Rectangle-1-Copy-3" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M14 152h113v31H14z"/><path id="Rectangle-1-Copy-5" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M14 183h113v31H14z"/><text id="Database" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="18" font-weight="normal"><tspan x="190" y="22">Database</tspan></text><text id="objectStore" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="18" font-weight="normal"><tspan x="18" y="53">objectStore</tspan></text><path id="Rectangle-1-Copy-10" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M182 93h113v31H182z"/><path id="Rectangle-1-Copy-9" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M182 124h113v31H182z"/><path id="Rectangle-1-Copy-8" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M182 155h113v31H182z"/><path id="Rectangle-1-Copy-7" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M182 186h113v31H182z"/><path id="Rectangle-1-Copy-6" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M182 217h113v31H182z"/><text id="objectStore" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="18" font-weight="normal"><tspan x="186" y="87">objectStore</tspan></text><path id="Rectangle-1-Copy-15" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M348 63h113v31H348z"/><path id="Rectangle-1-Copy-14" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M348 94h113v31H348z"/><path id="Rectangle-1-Copy-13" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M348 125h113v31H348z"/><text id="key3:-value3" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="22" y="140">key3: value3</tspan></text><text id="key2:-value2" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="22" y="111">key2: value2</tspan></text><text id="key4:-value4" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="22" y="171">key4: value4</tspan></text><text id="key5:-value5" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="22" y="202">key5: value5</tspan></text><path id="Rectangle-1-Copy-12" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M348 156h113v31H348z"/><path id="Rectangle-1-Copy-11" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2" d="M348 187h113v31H348z"/><text id="objectStore-Copy" fill="#AF6E24" font-family="OpenSans-Regular, Open Sans" font-size="18" font-weight="normal"><tspan x="352" y="57">objectStore</tspan></text><text id="key1:-value1" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="190" y="113">key1: value1</tspan></text><text id="key3:-value3" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="190" y="174">key3: value3</tspan></text><text id="key2:-value2" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="190" y="145">key2: value2</tspan></text><text id="key4:-value4" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="190" y="205">key4: value4</tspan></text><text id="key5:-value5" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="190" y="236">key5: value5</tspan></text><text id="key1:-value1" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="355" y="80">key1: value1</tspan></text><text id="key3:-value3" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="355" y="143">key3: value3</tspan></text><text id="key2:-value2" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="355" y="112">key2: value2</tspan></text><text id="key4:-value4" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="355" y="174">key4: value4</tspan></text><text id="key5:-value5" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="355" y="205">key5: value5</tspan></text></g></g></svg> \ No newline at end of file diff --git a/6-data-storage/index.md b/6-data-storage/index.md new file mode 100644 index 0000000000..6c76f3ce58 --- /dev/null +++ b/6-data-storage/index.md @@ -0,0 +1,2 @@ + +# ブラウザへのデータの保存 \ No newline at end of file diff --git a/7-animation/1-bezier-curve/article.md b/7-animation/1-bezier-curve/article.md new file mode 100644 index 0000000000..590402d336 --- /dev/null +++ b/7-animation/1-bezier-curve/article.md @@ -0,0 +1,194 @@ +# ベジェ曲線 + +ベジェ曲線は、CSSアニメーションやその他多くの場所で形状を描くために、コンピュータグラフィックスで使用されます。 + +実際には非常に単純なものですが、一度学んでおくと、ベクトルグラフィックスや高度なアニメーションの世界が受け入れやすくなるでしょう。 + +[cut] + +## 制御点(Control points) + +[ベジェ曲線](https://en.wikipedia.org/wiki/B%C3%A9zier_curve) は制御点/コントロールポイントによって定義されます。 + +制御点は 2, 3, 4 またはそれ以上あるかもしれません。 + +例えば、2つの制御点の曲線です: + +![](bezier2.svg) + +3つの制御点の曲線です: + +![](bezier3.svg) + +4点の曲線です: + +![](bezier4.svg) + +これらの曲線をよく見るとすぐに気づくでしょう: + +1. **点は必ずしも曲線上にあるわけではありません。** それは全く普通のことであり、後で曲線がどうやって作られるのかを見ていきます。 +2. **曲線の次数は点の数から1を引いた数になります。** 2つの点の場合は線形曲線(直線)であり、3つの点の場合 -- 2次曲線(放物線), 4つの点の場合は -- 3次曲線を持ちます。 +3. **曲線は常に制御点の[凸包(convex hull)](https://en.wikipedia.org/wiki/Convex_hull) の内側にあります。:** + + ![](bezier4-e.svg) ![](bezier3-e.svg) + +最後の性質により、コンピュータグラフィックスでは、交差テスト(intersection tests)を最適化することができます。凸包が交差しない場合、曲線もまた交差しません。したがって、凸包の交差を最初にチェックすることで、非常に高速な "交差なし" の結果を得ることができます。交差や凸包のチェックはより簡単です。なぜなら、それらは四角形、三角形など(上の図を参照)であり、曲線よりもはるかに単純な図形だからです。 + +描画のためのベジェ曲線の主な価値は -- 点を動かすことによって、曲線が *直感的に分かりやすい方法で* 変化するということです。 + +下の例で、マウスを使って制御点を移動させて見ましょう: + +[iframe src="demo.svg?nocpath=1&p=0,0,0.5,0,0.5,1,1,1" height=370] + +**お気づきのように、曲線は接線 1 -> 2 and 3 -> 4 に沿って伸びます。** + +いくつかの練習を経て、必要な曲線を得るための点の配置の仕方が明らかになります。そして、複数の曲線を繋ぐことで、実際になにかを得ることができます。 + +ここではいくつかの例です: + +![](bezier-car.svg) ![](bezier-letter.svg) ![](bezier-vase.svg) + +## Maths + +ベジェ曲線は数学的な式を使って記述することができます。 + +この後すぐに見ていきますが -- 知る必要はありません。が、完全性のためにここで説明します。 + +与えられた制御点の座標 <code>P<sub>i</sub></code>: 最初の制御点は座標 <code>P<sub>1</sub> = (x<sub>1</sub>, y<sub>1</sub>)</code> であり、2番目は: <code>P<sub>2</sub> = (x<sub>2</sub>, y<sub>2</sub>)</code> などとなり、曲線の座標は区分 `[0,1]` からのパラメータ `t` によって決まる式で記述されます。 + +- 2点の曲線の式: + + <code>P = (1-t)P<sub>1</sub> + tP<sub>2</sub></code> +- 3点の場合: + + <code>P = (1−t)<sup>2</sup>P<sub>1</sub> + 2(1−t)tP<sub>2</sub> + t<sup>2</sup>P<sub>3</sub></code> +- 4点の場合: + + <code>P = (1−t)<sup>3</sup>P<sub>1</sub> + 3(1−t)<sup>2</sup>tP<sub>2</sub> +3(1−t)t<sup>2</sup>P<sub>3</sub> + t<sup>3</sup>P<sub>4</sub></code> + +これらはベクトル方程式です。 + +これらを座標ごとに書き直すことができます。例えば、3点の曲線の場合: + +- <code>x = (1−t)<sup>2</sup>x<sub>1</sub> + 2(1−t)tx<sub>2</sub> + t<sup>2</sup>x<sub>3</sub></code> +- <code>y = (1−t)<sup>2</sup>y<sub>1</sub> + 2(1−t)ty<sub>2</sub> + t<sup>2</sup>y<sub>3</sub></code> + +<code>x<sub>1</sub>, y<sub>1</sub>, x<sub>2</sub>, y<sub>2</sub>, x<sub>3</sub>, y<sub>3</sub></code> の代わりに、3つの制御点の座標を入れます。 + +例えば、制御点が `(0,0)`, `(0.5, 1)` と `(1, 0)` の場合、方程式は次のようになります: + +- <code>x = (1−t)<sup>2</sup> * 0 + 2(1−t)t * 0.5 + t<sup>2</sup> * 1 = (1-t)t + t<sup>2</sup> = t</code> +- <code>y = (1−t)<sup>2</sup> * 0 + 2(1−t)t * 1 + t<sup>2</sup> * 0 = 2(1-t)t = –t<sup>2</sup> + 2t</code> + +今、 `t` が `0` から `1` まで実行されると、各 `t` の値 `(x,y)` の集合が曲線を形成します。 + +恐らくあまりに科学的な内容で、なぜ曲線がそのように見えるのか、そしてそれらが制御点にどのように依存するのかはあまり明白ではないと思います。 + +なので、ここでは理解しやすい描画アルゴリズムを紹介します。 + +## ド・カステリョのアルゴリズム + +[ド・カステリョ(De Casteljau's)のアルゴリズム](https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm) は曲線の数学的定義と同じですが、それがどのように構築されているかを視覚的に示しています。 + +3点の例を見てみましょう。 + +ここにデモとその説明が続きます。 + +点はマウスで動かすことができます。"play" ボタンを押して実行してみてください。 + +[iframe src="demo.svg?p=0,0,0.5,1,1,0&animate=1" height=370] + +**3点のベジェ曲線を構築するド・カステリョのアルゴリズムです。** + +1. 制御点を描画します。上のデモでは、それらは `1`, `2`, `3` とラベル付けされているものです。 +2. 制御点 1 -> 2 -> 3 の間に線分を作ります。上の例では、それらは <span style="color:#825E28">茶色の線</span> です。 +3. パラメータ `t` は `0` から `1` まで移動します。上の例では、一区切り `0.05` が使用されています。: ループは `0, 0.05, 0.1, 0.15, ... 0.95, 1` となります。 + + それぞれの `t` の値について: + + - 各 <span style="color:#825E28">茶色</span> の線分上では、その線の開始部分から `t` に比例した距離にある点を取ります。2つの線分があるので、2つの点があります。 + + 例えば、 `t=0` の場合 -- 両方の点は線分の先頭になり、`t=0.25` の場合は -- 開始から25%の線分の長さになります。`t=0.5` だと -- 50%(中央), `t=1` では -- 線分の終わりになります。 + + - 点を結びます。下の図では結んでいる線分は <span style="color:#167490">青</span> で描かれています。 + + +| `t=0.25` の場合 | `t=0.5` の場合 | +| ------------------------ | ---------------------- | +| ![](bezier3-draw1.svg) | ![](bezier3-draw2.svg) | + +4. 今、<span style="color:#167490">青</span> の線分は、同じ `t` の値に比例した距離で点を取ります。つまり、`t=0.25` (左図)の場合、線分の左端から1/4の位置に点があり、`t=0.5` (右図)の場合は -- 線分の中央に点があります。上の図では、点は <span style="color:red">赤</span> です。 + +5. `t` が `0` から `1` まで実行にするにつれて、`t` の各値は曲線に点を追加していきます。このような点の集合がベジェ曲線を形成します。それは上の図にある赤色の方物線です。 + +これは3点の場合の処理ですが、4点の場合も同様です。 + +4点の場合のデモです(マウスで点を移動させることができます): + +[iframe src="demo.svg?p=0,0,0.5,0,0.5,1,1,1&animate=1" height=370] + +アルゴリズム: + +- 制御点は 1 -> 2, 2 -> 3, 3 -> 4 の線分で結ばれています。ここでは、3つの <span style="color:#825E28">茶色</span> の線分があります。 +- `0` から `1` までの間隔における各 `t` に対して: + - 先頭から `t` に比例した距離で、それらの線分上の点を取ります。これらの点は結ばれ、2つの <span style="color:#0A0">緑の線分</span> ができます。 + - 緑の線分上で、`t` に比例した点を取ります。これで1つの <span style="color:#167490">青の線分</span> が得られます。 + - 青の線分上で `t` に比例した点を取ります。上の例では、それは <span style="color:red">赤</span> です。 +- これらの点が一緒に曲線を形成します。 + +このアルゴリズムは再帰的であり、任意の制御点の数で一般化できます。 + +与えられた N 個の制御点において、それらを結合し N-1 個の線分を得ます。 + +次に、`0` から `1` までのそれぞれの `t` について: +- 各線分上で `t` に比例した距離の点を取り、それらを結ぶと -- N-2 個の線分ができます。 +- N-2 個の線分に対して、`t` に比例した距離の点を取り、それらを結びます -- N-3 個のセグメントができます。 +- 1点だけを持つまで繰り返します。これらの点が曲線を作ります。 + +移動する曲線の例です: + +[iframe src="demo.svg?p=0,0,0,0.75,0.25,1,1,1&animate=1" height=370] + +その他の点の場合: + +[iframe src="demo.svg?p=0,0,1,0.5,0,0.5,1,1&animate=1" height=370] + +弧状の形: + +[iframe src="demo.svg?p=0,0,1,0.5,0,1,0.5,0&animate=1" height=370] + +滑らかではないベジェ曲線: + +[iframe src="demo.svg?p=0,0,1,1,0,1,1,0&animate=1" height=370] + +アルゴリズムが再帰的なので、5, 6, またはさらに多くの制御点を使用して、任意の順番のベジェ曲線を作ることができます。しかし、実際にはあまり有用ではありません。通常は 2, 3 の点を取り、複雑な線の場合は複数の曲線をくっつけます。開発と計算がよりシンプルになります。 + +```smart header="与えられた点を *通る* 曲線を描く方法?" +我々はベジェ曲線のために制御点を使います。ご覧の通り、制御点は曲線上にはありません。もしくはより正確に言うと最初と最後の制御点は曲線上にありますが、それ以外はありません。 + +別のタスクが必要なときもあります。: すべての制御点が単一の滑らかな曲線上にあるように、*複数の点を経由して* 曲線を描くというものです。このタスクは [補間/内挿](https://en.wikipedia.org/wiki/Interpolation) と呼ばれ、ここでは説明しません。 + +このような曲線の数式があります。例えば [ラグランジュ多項式](https://en.wikipedia.org/wiki/Lagrange_polynomial)です。 + +コンピュータグラフィックスでは、多くの点を結ぶ滑らかな曲線を構築するために[スプライン補間](https://en.wikipedia.org/wiki/Spline_interpolation) がよく使用されます。 +``` + +## サマリ + +ベジェ曲線は制御点により定義されます。 + +私たちはベジェ曲線の2つの定義を見てきました。 + +1. 数式を使った定義 +2. 描画プロセスを使った定義: ド・カステリョのアルゴリズム + +ベジェ曲線の良い特性: + + - 制御点を移動させることで、マウスで滑らかな線を描くことができます。 + - 複雑な形状は、いくつかのベジェ曲線で作ることができます。 + +利用法: + +- コンピュータグラフィックス、モデリング、ベクトルグラフィックエディタ。フォントはベジェ曲線で記述されています。 +- Web 開発では、-- Canvas 上のグラフィックスと SVG 形式で使われています。ちなみに、上の "ライブ" の例は SVG で書かれています。実際には、パラメータとして異なる点が与えられた単一のドキュメントです。別ウィンドウで開き、ソースを見ることができます: [demo.svg](demo.svg?p=0,0,1,0.5,0,0.5,1,1&animate=1). +- CSS アニメーションでは、アニメーションの経路とスピードを記述するために使われています。 diff --git a/7-animation/1-bezier-curve/bezier-car.svg b/7-animation/1-bezier-curve/bezier-car.svg new file mode 100644 index 0000000000..9f3f31f27d --- /dev/null +++ b/7-animation/1-bezier-curve/bezier-car.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="260" height="130" viewBox="0 0 260 130"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="car" fill="#D1CFCD" stroke="#181717" transform="translate(20 12)"><path id="Shape" stroke-width="2" d="M3.548 89.742C-8.28 60.978 15.376 43.72 74.516 37.968c5.914-11.506 8.871-23.011 8.871-34.516C183.925-8.054 228.28 20.71 216.452 89.742H3.548z"/><g id="crvpnt-link" transform="translate(0 86.29)"><ellipse id="crvpnt" cx="3.548" cy="3.452" rx="3.548" ry="3.452"/></g><g id="crvpnt-link" transform="translate(70.968 34.516)"><ellipse id="crvpnt" cx="3.548" cy="3.452" rx="3.548" ry="3.452"/></g><g id="crvpnt-link" transform="translate(79.839)"><ellipse id="crvpnt" cx="3.548" cy="3.452" rx="3.548" ry="3.452"/></g><g id="crvpnt-link" transform="translate(212.903 86.29)"><ellipse id="crvpnt" cx="3.548" cy="3.452" rx="3.548" ry="3.452"/></g><g id="wheel-link" stroke-width="5" transform="translate(39.032 72.484)"><ellipse id="wheel" cx="17.742" cy="17.258" rx="17.742" ry="17.258"/></g><g id="wheel-link" stroke-width="5" transform="translate(154.355 72.484)"><ellipse id="wheel" cx="17.742" cy="17.258" rx="17.742" ry="17.258"/></g></g></g></svg> \ No newline at end of file diff --git a/7-animation/1-bezier-curve/bezier-letter.svg b/7-animation/1-bezier-curve/bezier-letter.svg new file mode 100644 index 0000000000..d384cc7acf --- /dev/null +++ b/7-animation/1-bezier-curve/bezier-letter.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="166" height="173" viewBox="0 0 166 173"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="blue-zones" transform="translate(13 3)"><g id="g10" transform="matrix(1 0 0 -1 .167 164.907)"><g id="g12" transform="translate(.083 .015)"><g id="g14"><g id="g24" transform="translate(0 .778)"><path id="path30" fill="#D1CFCD" d="M94.195 31.581c1.276-13.808 9.886-28.307 24.875-28.307 6.697 0 26.15 4.833 26.15 32.795v19.332h-7.973V36.07c0-20.022-7.973-22.094-11.48-22.094-10.524 0-11.8 15.535-11.8 17.261v69.043c0 14.5 0 27.963-11.48 40.736-12.438 13.463-28.383 18.987-43.69 18.987-26.15 0-48.155-16.225-48.155-39.01 0-10.356 6.378-16.225 14.67-16.225 8.93 0 14.67 6.904 14.67 15.88 0 4.143-1.595 15.535-16.265 15.88 8.61 12.083 24.237 15.88 34.442 15.88 15.626 0 33.804-13.463 33.804-44.188V95.446c-16.264-1.035-38.588-2.071-58.679-12.427C9.367 71.28 1.394 53.329 1.394 38.14c0-27.962 30.934-36.592 51.025-36.592 21.047 0 35.717 13.808 41.776 30.033z"/><path id="path32" fill="#FFF" d="M91.963 88.197V53.675c0-32.795-22.961-44.533-37.312-44.533-15.626 0-28.701 12.083-28.701 29.344 0 18.987 13.394 47.64 66.013 49.71z"/><path id="path34" fill="#181717" d="M93.07 30.363h2.25V32.8h-2.25z"/><path id="path36" fill="#181717" d="M117.945 2.056h2.25V4.49h-2.25z"/><path id="path38" fill="#181717" d="M144.095 34.851h2.25v2.436h-2.25z"/><path id="path40" fill="#181717" d="M144.095 54.183h2.25v2.436h-2.25z"/><path id="path42" fill="#181717" d="M136.122 54.183h2.25v2.436h-2.25z"/><path id="path44" fill="#181717" d="M136.122 34.851h2.25v2.436h-2.25z"/><path id="path46" fill="#181717" d="M124.641 12.757h2.25v2.436h-2.25v-2.436z"/><path id="path48" fill="#181717" d="M112.842 30.018h2.25v2.436h-2.25z"/><path id="path50" fill="#181717" d="M112.842 99.061h2.25v2.436h-2.25z"/><path id="path52" fill="#181717" d="M101.362 139.797h2.25v2.436h-2.25z"/><path id="path54" fill="#181717" d="M57.672 158.784h2.25v2.436h-2.25z"/><path id="path56" fill="#181717" d="M9.517 119.774h2.25v2.436h-2.25z"/><path id="path58" fill="#181717" d="M24.187 103.55h2.25v2.435h-2.25z"/><path id="path60" fill="#181717" d="M38.856 119.43h2.25v2.435h-2.25z"/><path id="path62" fill="#181717" d="M22.592 135.31h2.25v2.435h-2.25z"/><path id="path64" fill="#181717" d="M57.034 151.19h2.25v2.435h-2.25z"/><path id="path66" fill="#181717" d="M90.838 107.001h2.25v2.436h-2.25z"/><path id="path68" fill="#181717" d="M90.838 94.228h2.25v2.436h-2.25z"/><path id="path70" fill="#181717" d="M32.16 81.8h2.25v2.436h-2.25v-2.435z"/><path id="path72" fill="#181717" d="M.269 36.923h2.25v2.435H.269z"/><path id="path74" fill="#181717" d="M51.294.33h2.25v2.435h-2.25z"/><path id="path76" fill="#181717" d="M93.07 30.363h2.25V32.8h-2.25z"/><path id="path78" fill="#181717" d="M90.838 86.979h2.25v2.436h-2.25z"/><path id="path80" fill="#181717" d="M90.838 52.457h2.25v2.436h-2.25v-2.436z"/><path id="path82" fill="#181717" d="M53.526 7.924h2.25v2.436h-2.25z"/><path id="path84" fill="#181717" d="M24.825 37.268h2.25v2.435h-2.25z"/><path id="path86" fill="#181717" d="M90.838 86.979h2.25v2.436h-2.25z"/></g></g></g></g></g></g></svg> \ No newline at end of file diff --git a/7-animation/1-bezier-curve/bezier-vase.svg b/7-animation/1-bezier-curve/bezier-vase.svg new file mode 100644 index 0000000000..ccc09690a9 --- /dev/null +++ b/7-animation/1-bezier-curve/bezier-vase.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="110" height="170" viewBox="0 0 110 170"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="figure4-15" transform="translate(5 1)"><path id="Shape" stroke="#AF6E24" stroke-width="2" d="M32.01 161.209c-19.503-39.602-19.503-79.204 0-118.806 6.5-13.2 6.5-26.401 0-39.602h39.005c-6.5 13.2-6.5 26.401 0 39.602 19.502 39.602 19.502 79.204 0 118.806H32.01z"/><path id="Shape" stroke="#181717" stroke-dasharray="2" stroke-width=".5" d="M32.01 2.801l9.751 19.801-39.005 79.204 29.254 59.403M71.015 2.801l-9.751 19.801 39.005 79.204-29.254 59.403"/><g id="dot-link" fill="#FFF" stroke="#181717" transform="translate(49.562 .82)"><ellipse id="dot" cx="1.95" cy="1.98" rx="1.95" ry="1.98"/></g><g id="dot-link" fill="#FFF" stroke="#181717" transform="translate(30.06 .82)"><ellipse id="dot" cx="1.95" cy="1.98" rx="1.95" ry="1.98"/></g><g id="dot-link" fill="#FFF" stroke="#181717" transform="translate(39.81 20.622)"><ellipse id="dot" cx="1.95" cy="1.98" rx="1.95" ry="1.98"/></g><g id="dot-link" fill="#FFF" stroke="#181717" transform="translate(30.06 40.423)"><ellipse id="dot" cx="1.95" cy="1.98" rx="1.95" ry="1.98"/></g><g id="dot-link" fill="#FFF" stroke="#181717" transform="translate(.806 99.826)"><ellipse id="dot" cx="1.95" cy="1.98" rx="1.95" ry="1.98"/></g><g id="dot-link" fill="#FFF" stroke="#181717" transform="translate(30.06 159.229)"><ellipse id="dot" cx="1.95" cy="1.98" rx="1.95" ry="1.98"/></g><g id="dot-link" fill="#FFF" stroke="#181717" transform="translate(69.065 .82)"><ellipse id="dot" cx="1.95" cy="1.98" rx="1.95" ry="1.98"/></g><g id="dot-link" fill="#FFF" stroke="#181717" transform="translate(59.313 20.622)"><ellipse id="dot" cx="1.95" cy="1.98" rx="1.95" ry="1.98"/></g><g id="dot-link" fill="#FFF" stroke="#181717" transform="translate(69.065 40.423)"><ellipse id="dot" cx="1.95" cy="1.98" rx="1.95" ry="1.98"/></g><g id="dot-link" fill="#FFF" stroke="#181717" transform="translate(98.318 99.826)"><ellipse id="dot" cx="1.95" cy="1.98" rx="1.95" ry="1.98"/></g><g id="dot-link" fill="#FFF" stroke="#181717" transform="translate(69.065 159.229)"><ellipse id="dot" cx="1.95" cy="1.98" rx="1.95" ry="1.98"/></g></g></g></svg> \ No newline at end of file diff --git a/7-animation/1-bezier-curve/bezier2.svg b/7-animation/1-bezier-curve/bezier2.svg new file mode 100644 index 0000000000..b8dfe8f26d --- /dev/null +++ b/7-animation/1-bezier-curve/bezier2.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="149" height="187" viewBox="0 0 149 187"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="bezier2.svg"><path id="Path-6" stroke="#A7333A" stroke-width="2" d="M24.17 143.348l100.144-99.843"/><circle id="Oval-1" cx="24" cy="144" r="4" fill="#FFF" stroke="#DBAF88"/><text id="1" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="20" y="165">1</tspan></text><circle id="Oval-2" cx="124" cy="44" r="4" fill="#FFF" stroke="#DBAF88"/><text id="2" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="120.101" y="65">2</tspan></text></g></g></svg> \ No newline at end of file diff --git a/7-animation/1-bezier-curve/bezier3-draw1.svg b/7-animation/1-bezier-curve/bezier3-draw1.svg new file mode 100644 index 0000000000..fd3ca092fd --- /dev/null +++ b/7-animation/1-bezier-curve/bezier3-draw1.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="340" height="350" viewBox="0 0 340 350"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="bezier3-draw1.svg"><path id="Path-8" stroke="#DBAF88" d="M37.282 314.328L171.238 46.82l134.966 268.494"/><path id="Path-7" stroke="#A7333A" stroke-width="2" d="M37.738 314.328c98.312-197.562 188.187-157.862 268.157 1.49"/><circle id="Oval-1" cx="37" cy="316" r="4" fill="#FFF" stroke="#DBAF88"/><text id="1" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="33" y="337">1</tspan></text><circle id="Oval-3" cx="306" cy="316" r="4" fill="#FFF" stroke="#DBAF88"/><text id="3" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="302" y="337">3</tspan></text><circle id="Oval-2" cx="171" cy="47" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-4" cx="104" cy="215" r="4" fill="#A7333A"/><text id="2" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="167" y="39">2</tspan></text><text id=".25" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="100" y="238">0.25</tspan></text><text id="t-=-0.25" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="12" y="254.849">t = 0.25</tspan></text><text id=".25" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="209" y="112">0.25</tspan></text><path id="Line" stroke="#1C85B5" stroke-linecap="square" stroke-width="2" d="M70.5 247.5l134-133"/><image id="ease-in" width="133" height="135" x="231" y="-133" xlink:href="data:image/png;base64,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"/></g></g></svg> \ No newline at end of file diff --git a/7-animation/1-bezier-curve/bezier3-draw2.svg b/7-animation/1-bezier-curve/bezier3-draw2.svg new file mode 100644 index 0000000000..4afe044541 --- /dev/null +++ b/7-animation/1-bezier-curve/bezier3-draw2.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="340" height="350" viewBox="0 0 340 350"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="Group" transform="translate(33 24)"><path id="Path-8" stroke="#DBAF88" d="M4.282 290.328L138.238 22.82l134.966 268.494"/><path id="Path-7" stroke="#A7333A" stroke-width="2" d="M4.738 290.328c98.312-197.562 188.187-157.862 268.157 1.49"/><circle id="Oval-1" cx="4" cy="292" r="4" fill="#FFF" stroke="#DBAF88"/><text id="1" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="0" y="313">1</tspan></text><circle id="Oval-3" cx="273" cy="292" r="4" fill="#FFF" stroke="#DBAF88"/><text id="3" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="269" y="313">3</tspan></text><circle id="Oval-2" cx="138" cy="23" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-4" cx="138" cy="157" r="4" fill="#A7333A"/><path id="Line" stroke="#1C85B5" stroke-linecap="square" stroke-width="2" d="M71.5 157H205"/><text id="2" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="134" y="15">2</tspan></text><text id=".5" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="128" y="181">0.5</tspan></text><text id="t-=-0.5" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="17" y="163">t = 0.5</tspan></text><text id=".5" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="216" y="163">0.5</tspan></text></g></g></svg> \ No newline at end of file diff --git a/7-animation/1-bezier-curve/bezier3-e.svg b/7-animation/1-bezier-curve/bezier3-e.svg new file mode 100644 index 0000000000..4c5c741dc3 --- /dev/null +++ b/7-animation/1-bezier-curve/bezier3-e.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="149" height="187" viewBox="0 0 149 187"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="bezier3-e.svg"><path id="Triangle-1" stroke="#DBAF88" d="M74.5 43L125 144H24z"/><path id="Path-4" stroke="#A7333A" stroke-width="2" d="M24.279 143.124c50.221-100.184 89.93-17.718 99.822.521"/><circle id="Oval-1" cx="24" cy="144" r="4" fill="#FFF" stroke="#DBAF88"/><text id="1" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="20" y="165">1</tspan></text><circle id="Oval-2" cx="124" cy="144" r="4" fill="#FFF" stroke="#DBAF88"/><text id="3" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="120" y="165">3</tspan></text><circle id="Oval-3" cx="74" cy="44" r="4" fill="#FFF" stroke="#DBAF88"/><text id="-4" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="70.19" y="36">3</tspan></text></g></g></svg> \ No newline at end of file diff --git a/7-animation/1-bezier-curve/bezier3.svg b/7-animation/1-bezier-curve/bezier3.svg new file mode 100644 index 0000000000..35f1eb6e39 --- /dev/null +++ b/7-animation/1-bezier-curve/bezier3.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="149" height="187" viewBox="0 0 149 187"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="bezier3.svg"><path id="Path-4" stroke="#A7333A" stroke-width="2" d="M24.279 143.124c50.221-100.184 89.93-17.718 99.822.521"/><circle id="Oval-1" cx="24" cy="144" r="4" fill="#FFF" stroke="#DBAF88"/><text id="1" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="20" y="165">1</tspan></text><circle id="Oval-2" cx="124" cy="144" r="4" fill="#FFF" stroke="#DBAF88"/><text id="3" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="120" y="165">3</tspan></text><circle id="Oval-3" cx="74" cy="44" r="4" fill="#FFF" stroke="#DBAF88"/><text id="2" fill="#AF6E24" font-family="PTMono-Regular, PT Mono" font-size="14" font-weight="normal"><tspan x="69.99" y="32">2</tspan></text></g></g></svg> \ No newline at end of file diff --git a/7-animation/1-bezier-curve/bezier4-e.svg b/7-animation/1-bezier-curve/bezier4-e.svg new file mode 100644 index 0000000000..2e2bed1bac --- /dev/null +++ b/7-animation/1-bezier-curve/bezier4-e.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="149" height="187" viewBox="0 0 149 187"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="bezier4-e.svg"><path id="Rectangle-1" stroke="#DBAF88" d="M122.715 43.5l-50.2 100H23.813l50.2-100h48.701z"/><path id="Path-1" stroke="#A7333A" stroke-width="2" d="M23.688 143.905c50.015 0 50.703-100.414 100.467-100.414"/><circle id="Oval-1" cx="24" cy="144" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-2" cx="124" cy="43" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-3" cx="73" cy="43" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-4" cx="73" cy="143" r="4" fill="#FFF" stroke="#DBAF88"/><text id="1" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="20" y="165">1</tspan></text><text id="2" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="69" y="165">2</tspan></text><text id="3" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="69" y="35">3</tspan></text><text id="4" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="121" y="35">4</tspan></text></g></g></svg> \ No newline at end of file diff --git a/7-animation/1-bezier-curve/bezier4.svg b/7-animation/1-bezier-curve/bezier4.svg new file mode 100644 index 0000000000..551184d1b9 --- /dev/null +++ b/7-animation/1-bezier-curve/bezier4.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="149" height="187" viewBox="0 0 149 187"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="bezier4.svg"><path id="Path-1" stroke="#A7333A" stroke-width="2" d="M23.688 143.905c50.015 0 50.703-100.414 100.467-100.414"/><circle id="Oval-1" cx="24" cy="144" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-2" cx="124" cy="43" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-3" cx="73" cy="43" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-4" cx="73" cy="143" r="4" fill="#FFF" stroke="#DBAF88"/><text id="1" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="20" y="165">1</tspan></text><text id="2" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="69" y="165">2</tspan></text><text id="3" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="69" y="35">3</tspan></text><text id="4" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="121" y="35">4</tspan></text></g></g></svg> \ No newline at end of file diff --git a/7-animation/1-bezier-curve/demo.svg b/7-animation/1-bezier-curve/demo.svg new file mode 100644 index 0000000000..5240697ee5 --- /dev/null +++ b/7-animation/1-bezier-curve/demo.svg @@ -0,0 +1,277 @@ +<?xml version="1.0" ?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/SVG/DTD/svg10.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:xlink="http://www.w3.org/1999/xlink" width="340" height="360"> +<!--+ no-optimize --> +<!-- +@see +http://www.learnsvg.com/books/learnsvg/html/bitmap/chapter04/page04-1.php +http://apike.ca/prog_svg_paths.html +http://www.the-art-of-web.com/css/css-animation/ +http://srufaculty.sru.edu/david.dailey/svg/curve.svg +http://www.w3.org/Graphics/SVG/IG/resources/svgprimer.html#path_C +--> +<defs> +<pattern id="Pat01" width="10" height="10" patternUnits="userSpaceOnUse"> + <rect width="10" height="10" fill="#FFFFFF" stroke="#000000" stroke-width="0.1"/> +</pattern> +</defs> + + +<rect x="20" y="50" width="300" height="300" fill="url(#Pat01)"/> + + +<path d="M20,50 H320 V350 H20 Z" fill="none" stroke="none" stroke-width="0.5" stroke-dasharray="2 2"/> + + +<path fill="none" id="control-path" stroke="#E8C48E"/> +<path stroke="red" fill="none" id="bezier-path" stroke-width="1.2"/> + +<path d="" fill="none" id="t-path-template"/> + +<svg x="0" y="0" id="p-template"> +<circle r="4" fill="white" stroke="#E8C48E" stroke-width="1" cx="20" cy="20" style="cursor: pointer"/> +<text x="12" y="12" style="font-size:16px;font-weight:bold">1</text> +</svg> + +<circle r="4" fill="red" stroke="red" id="marker" stroke-width="1" cx="-10" cy="-10"/> + +<svg> +<image xlink:href="play.png" x="5" y="5" style="cursor:pointer" width="30" height="30" id="run-button"/> +<text x="38" y="27" style="font-size:18px;font-family:'DejaVu Sans Mono', 'Lucida Console', 'Menlo', 'Monaco', Lucida Console, sans-serif">t:<tspan id="t-value">1</tspan></text> +</svg> + +<script><![CDATA[ + let xmlns = "http://www.w3.org/2000/svg"; + let xlinkns = "http://www.w3.org/1999/xlink"; + + let box = {left:20, top: 50, right: 320, bottom: 350 }; + + if (location.search.match('small=1')) { + box = {left:20, top: 50, right: 120, bottom: 150 }; + } + + document.documentElement.ondragstart = function() { + return false; + }; + + let pointsIn = location.search.match(/p=(.*?)($|&)/)[1]; + + // don't show a piece of control path between P2 and P3 + let hideMiddleControlPath = location.search.match('nocpath=1'); + + pointsIn = pointsIn.split(','); + + let points = []; + + // read points from ?p=... + for(let i=0; i<pointsIn.length; i++) { + let x = box.left + (box.right - box.left)*pointsIn[i]; + let y = box.bottom + (box.top - box.bottom)*pointsIn[++i]; + points.push({x: x, y: y}); + } + + let bezierPath = document.getElementById("bezier-path"); + let controlPath = document.getElementById("control-path"); + //controlPath.style.display = location.search.match('nocpath=1') ? 'none' : 'block'; + + let runButton = document.getElementById("run-button"); + runButton.parentNode.style.display = location.search.match('animate=1') ? 'block' : 'none'; + + + + let tPathTemplate = document.getElementById("t-path-template"); + tPathTemplate.parentNode.removeChild(tPathTemplate); + + function drawPath() { + let letter; + switch (points.length) { + case 4: + letter = 'C'; + break; + case 3: + letter = 'Q'; + break; + default: + letter = 'L'; + } + let bezierPathD = "M" + points[0].x + ',' + points[0].y + ' '+letter; + let controlPathD = "M" + points[0].x + ',' + points[0].y + ' L'; + + for(let i = 1; i < points.length; i++) { + bezierPathD += points[i].x + ',' + points[i].y + ' '; + controlPathD += points[i].x + ',' + points[i].y + ' '; + } + bezierPath.setAttribute('d', bezierPathD); + controlPath.setAttribute('d', controlPathD); + + function dist(a,b) { + return Math.round(Math.sqrt( (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y))); + } + + if (hideMiddleControlPath) { + controlPath.setAttribute('stroke-dasharray', dist(points[0],points[1])+' '+dist(points[1],points[2])+' 9999 9999'); + } + + } + + // draw control points + function drawPoints(points) { + let p = document.getElementById('p-template'); + p.removeAttribute('id'); + p.parentNode.removeChild(p); + + for(let i=0; i<points.length; i++) { + let point = p.cloneNode(true); + point.getElementsByTagName('text')[0].firstChild.data = i+1; + setPointCoords(point, i); + setPointHandler(point, i); + document.documentElement.appendChild(point); + } + + } + + // control points coords are shifted from Left-Upper corner a bit + // to give them space to render + function setPointCoords(point, i) { + point.setAttribute('x', points[i].x-20); + point.setAttribute('y', points[i].y-20); + } + + function setPointHandler(point, i) { + let circle = point.getElementsByTagName('circle')[0]; + circle.onmousedown = function() { + document.onmousemove = function(e) { + let x = e.pageX, y = e.pageY; + + // constrain withing the box + if (x < box.left) x = box.left; + if (x > box.right) x = box.right; + if (y > box.bottom) y = box.bottom; + if (y < box.top) y = box.top; + + points[i].x = x; + points[i].y = y; + setPointCoords(point, i); + drawPath(); + } + document.onmouseup = function() { + document.onmousemove = document.onmouseup = null; + } + return false; + } + } + + // draw point connectors which form the curve + function drawT(points, t) { + let path = document.getElementById('t-'+points.length); + + if (!path) { + path = tPathTemplate.cloneNode(true); + path.setAttribute('stroke', ["blue","#0a0","red"][points.length % 3]); + path.setAttribute('id', 't-'+points.length); + document.documentElement.appendChild(path); + } + + let subPoints = []; + let x = points[0].x + (points[1].x - points[0].x)*t; + let y = points[0].y + (points[1].y - points[0].y)*t; + + let tPathD = "M" + x + ',' + y + ' L'; + subPoints.push({x, y}); + + for(let i=1; i<points.length-1; i++) { + let x = points[i].x + (points[i+1].x - points[i].x)*t; + let y = points[i].y + (points[i+1].y - points[i].y)*t; + subPoints.push({x: x, y: y}); + + tPathD += x + ',' + y + ' '; + } + + if (points.length <= 3) { + let m = document.getElementById('marker'); + let mx, my; + if (t == 1) { + mx = -10, my = -10; + } else { + mx = subPoints[0].x + (subPoints[1].x-subPoints[0].x)*t; + my = subPoints[0].y + (subPoints[1].y-subPoints[0].y)*t; + } + m.setAttribute('cx', mx); + m.setAttribute('cy', my); + path.setAttribute('stroke-width', 2); + } + if (points.length == 2) { + // only 2 points provided, special case + tPathD = "M"+points[0].x+","+points[0].y+" L"+x+","+y; + } + + path.setAttribute('d', tPathD); + + if (subPoints.length > 2){ + drawT(subPoints, t); + } + } + + let t = 0; + let timer; + + + // animate the curve + function animate(complete) { + + timer = setInterval(function() { + if (t>1) { + t = 1; + } + //bezierPath.setAttribute('stroke-dasharray', t*bezierPath.getTotalLength()+' '+bezierPath.getTotalLength()); + drawT(points, t); + + document.getElementById('t-value').firstChild.nodeValue = t==0 ? 0 : t == 1 ? 1 : t.toFixed(3); + /* + if (t >= 0.5) { + clearInterval(timer); + runButton.parentNode.style.display = 'none'; + return; + } + */ + if (t == 1) { + clearInterval(timer); + timer = 0; + complete && complete(); + return; + } + + t += 0.005; + }, 30); + } + + function animationDone() { + for(let i=1; i<=points.length; i++) { + let path = document.getElementById('t-'+i); + path && document.documentElement.removeChild(path); + } + t = 0; + runButton.setAttributeNS(xlinkns, 'xlink:href', 'play.png'); + } + + runButton.onclick = onAnimate; + + function onAnimate() { + if (timer) { + // animation in action + runButton.setAttributeNS(xlinkns, 'xlink:href', 'play.png'); + clearInterval(timer); + timer = 0; + return; + } + + runButton.setAttributeNS(xlinkns, 'xlink:href', 'pause.png'); + animate(animationDone) + } + + drawPath(); + drawPoints(points); + + +//]]></script> +</svg> diff --git a/3-animation/1-bezier-curve/pause.png b/7-animation/1-bezier-curve/pause.png similarity index 100% rename from 3-animation/1-bezier-curve/pause.png rename to 7-animation/1-bezier-curve/pause.png diff --git a/3-animation/1-bezier-curve/play.png b/7-animation/1-bezier-curve/play.png similarity index 100% rename from 3-animation/1-bezier-curve/play.png rename to 7-animation/1-bezier-curve/play.png diff --git a/3-animation/2-css-animations/1-animate-logo-css/solution.md b/7-animation/2-css-animations/1-animate-logo-css/solution.md similarity index 100% rename from 3-animation/2-css-animations/1-animate-logo-css/solution.md rename to 7-animation/2-css-animations/1-animate-logo-css/solution.md diff --git a/3-animation/2-css-animations/1-animate-logo-css/solution.view/index.html b/7-animation/2-css-animations/1-animate-logo-css/solution.view/index.html similarity index 100% rename from 3-animation/2-css-animations/1-animate-logo-css/solution.view/index.html rename to 7-animation/2-css-animations/1-animate-logo-css/solution.view/index.html diff --git a/3-animation/2-css-animations/1-animate-logo-css/source.view/index.html b/7-animation/2-css-animations/1-animate-logo-css/source.view/index.html similarity index 100% rename from 3-animation/2-css-animations/1-animate-logo-css/source.view/index.html rename to 7-animation/2-css-animations/1-animate-logo-css/source.view/index.html diff --git a/3-animation/2-css-animations/1-animate-logo-css/task.md b/7-animation/2-css-animations/1-animate-logo-css/task.md similarity index 100% rename from 3-animation/2-css-animations/1-animate-logo-css/task.md rename to 7-animation/2-css-animations/1-animate-logo-css/task.md diff --git a/7-animation/2-css-animations/2-animate-logo-bezier-css/bezier-up.svg b/7-animation/2-css-animations/2-animate-logo-bezier-css/bezier-up.svg new file mode 100644 index 0000000000..8275210796 --- /dev/null +++ b/7-animation/2-css-animations/2-animate-logo-bezier-css/bezier-up.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="149" height="187" viewBox="0 0 149 187"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="bezier-up.svg"><path id="Line-3" stroke="#7E7C7B" stroke-dasharray="3,2,3,3" stroke-linecap="square" d="M12.5 60.5h123" opacity=".7"/><path id="Line" stroke="#DBAF88" stroke-linecap="square" d="M24.5 160.5l25-147"/><path id="Line-2" stroke="#DBAF88" stroke-linecap="square" d="M124.5 60.5l-25-51"/><path id="bezier-path" stroke="#A7333A" stroke-width="2" d="M23.667 160.667c25-150 75-150 100-100"/><circle id="Oval-1" cx="24" cy="161" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-2" cx="124" cy="60" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-4" cx="49" cy="11" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-5" cx="99" cy="11" r="4" fill="#FFF" stroke="#DBAF88"/><text id="1" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="20" y="182">1</tspan></text><text id="2" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="33" y="16">2</tspan></text><text id="3" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="106" y="16">3</tspan></text><text id="4" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="127" y="52">4</tspan></text></g></g></svg> \ No newline at end of file diff --git a/7-animation/2-css-animations/2-animate-logo-bezier-css/solution.md b/7-animation/2-css-animations/2-animate-logo-bezier-css/solution.md new file mode 100644 index 0000000000..87a3f6a7eb --- /dev/null +++ b/7-animation/2-css-animations/2-animate-logo-bezier-css/solution.md @@ -0,0 +1,7 @@ +このアニメーションに対する正しいベジェ曲線を選ぶ必要があります。"飛び出す" ようにするため、どこかで `y>1` となるタイミングが必要です。 + +例えば、`cubic-bezier(0.25, 1.5, 0.75, 1.5)` のように、両方の制御点が `y>1` を取ることができます。 + +グラフは次の通りです: + +![](bezier-up.svg) diff --git a/3-animation/2-css-animations/2-animate-logo-bezier-css/solution.view/index.html b/7-animation/2-css-animations/2-animate-logo-bezier-css/solution.view/index.html similarity index 100% rename from 3-animation/2-css-animations/2-animate-logo-bezier-css/solution.view/index.html rename to 7-animation/2-css-animations/2-animate-logo-bezier-css/solution.view/index.html diff --git a/3-animation/2-css-animations/2-animate-logo-bezier-css/task.md b/7-animation/2-css-animations/2-animate-logo-bezier-css/task.md similarity index 100% rename from 3-animation/2-css-animations/2-animate-logo-bezier-css/task.md rename to 7-animation/2-css-animations/2-animate-logo-bezier-css/task.md diff --git a/7-animation/2-css-animations/3-animate-circle/solution.md b/7-animation/2-css-animations/3-animate-circle/solution.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/3-animation/2-css-animations/3-animate-circle/solution.view/index.html b/7-animation/2-css-animations/3-animate-circle/solution.view/index.html similarity index 100% rename from 3-animation/2-css-animations/3-animate-circle/solution.view/index.html rename to 7-animation/2-css-animations/3-animate-circle/solution.view/index.html diff --git a/3-animation/2-css-animations/3-animate-circle/source.view/index.html b/7-animation/2-css-animations/3-animate-circle/source.view/index.html similarity index 100% rename from 3-animation/2-css-animations/3-animate-circle/source.view/index.html rename to 7-animation/2-css-animations/3-animate-circle/source.view/index.html diff --git a/3-animation/2-css-animations/3-animate-circle/task.md b/7-animation/2-css-animations/3-animate-circle/task.md similarity index 100% rename from 3-animation/2-css-animations/3-animate-circle/task.md rename to 7-animation/2-css-animations/3-animate-circle/task.md diff --git a/7-animation/2-css-animations/4-animate-circle-callback/solution.md b/7-animation/2-css-animations/4-animate-circle-callback/solution.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/6-async/01-callbacks/01-animate-circle-callback/solution.view/index.html b/7-animation/2-css-animations/4-animate-circle-callback/solution.view/index.html similarity index 100% rename from 6-async/01-callbacks/01-animate-circle-callback/solution.view/index.html rename to 7-animation/2-css-animations/4-animate-circle-callback/solution.view/index.html diff --git a/6-async/01-callbacks/01-animate-circle-callback/task.md b/7-animation/2-css-animations/4-animate-circle-callback/task.md similarity index 100% rename from 6-async/01-callbacks/01-animate-circle-callback/task.md rename to 7-animation/2-css-animations/4-animate-circle-callback/task.md diff --git a/7-animation/2-css-animations/article.md b/7-animation/2-css-animations/article.md new file mode 100644 index 0000000000..f16ef5d760 --- /dev/null +++ b/7-animation/2-css-animations/article.md @@ -0,0 +1,423 @@ +# CSS アニメーション + +CSS アニメーションは JavaScript を使うことなく簡単なアニメーションを行うことができます。 + +JavaScript を利用することで、CSS アニメーションを制御し、少しのコードでより優れたものにすることができます。 + +[cut] + +## CSS のトランジション + +CSS トランジションの考えはシンプルです。これから、そのプロパティとその変化がどのようにアニメーション化されるかを説明します。プロパティが変更されると、ブラウザはアニメーションを描写します。 + +つまり: 必要なことはプロパティを変更することだけです。そして滑らかなトランジションはブラウザによって行われます。 + +例えば、下の CSS は `background-color` の変化を 3秒間アニメーション化します。: + +```css +.animated { + transition-property: background-color; + transition-duration: 3s; +} +``` + +今、ある要素が `.animated` クラスを持っている場合、`background-color` の変更は3秒間でアニメーションされます。 + +下のボタンをクリックして、背景をアニメーションさせてみてください。: + +```html run autorun height=60 +<button id="color">Click me</button> + +<style> + #color { + transition-property: background-color; + transition-duration: 3s; + } +</style> + +<script> + color.onclick = function() { + this.style.backgroundColor = 'red'; + }; +</script> +``` + +CSS トランジションを記述するのに 4 つのプロパティがあります: + +- `transition-property` +- `transition-duration` +- `transition-timing-function` +- `transition-delay` + +この後説明していきますが、今の時点では、共通の `transition` プロパティは `property duration timing-function delay` の順番で一緒に宣言できること、複数のプロパティを一度にアニメーションすることができることに留意しておいてください。 + +例えば、このボタンは `color` と `font-size` をアニメーションします。: + +```html run height=80 autorun no-beautify +<button id="growing">Click me</button> + +<style> +#growing { +*!* + transition: font-size 3s, color 2s; +*/!* +} +</style> + +<script> +growing.onclick = function() { + this.style.fontSize = '36px'; + this.style.color = 'red'; +}; +</script> +``` + +ではアニメーションのプロパティを1つずつ見ていきましょう。 + +## transition-property + +`transition-property` には、アニメーションするプロパティの一覧を記載します。例えば: `left`, `margin-left`, `height`, `color` です。 + +すべてのプロパティがアニメーションできるわけではありませんが、[それらの多くが可能です](http://www.w3.org/TR/css3-transitions/#animatable-properties-)。値 `all` は "すべてのプロパティをアニメートする" を意味します。 + +## transition-duration + +`transition-duration` では、どのくらいの長さアニメーションをするかを指定することができます。時間は [CSS 時間形式](http://www.w3.org/TR/css3-values/#time) で表します: 秒は `s`, ミリ秒は `ms` です。 + +## transition-delay + +`transition-delay` では、アニメーションする *前* の遅延を指定することができます。例えば、`transition-delay: 1s` を指定した場合、アニメーションはある変更の1秒後に始まります。 + +負の値も可能です。その場合、アニメーションは途中から始まります。例えば、`transition-duration` が `2s` で、遅延が `-1s` の場合、アニメーションは1秒を取り、半分から開始します。 + +これは CSS `translate` プロパティを使って、`0` から `9` までの数字をシフトするアニメーションです: + +[codetabs src="digits"] + +`transform` プロパティは次のようにアニメーションされます: + +```css +#stripe.animate { + transform: translate(-90%); + transition-property: transform; + transition-duration: 9s; +} +``` + +上の例では、JavaScript は要素にクラス `.animate` を追加し -- それによりアニメーションを開始しています。: + +```js +stripe.classList.add('animate'); +``` + +"途中から" 始めることも可能です。負の値 `transition-delay` を使って、例えば、現在の秒数に対応する正確な数値から始めることができます。 + +ここでは、数字をクリックすると -- 現在の秒数からアニメーションが始まります。: + +[codetabs src="digits-negative-delay"] + +JavaScript は追加の行でそれをしています。: + +```js +stripe.onclick = function() { + let sec = new Date().getSeconds() % 10; +*!* + // 例えば、ここで -3s は3番目からアニメーションを開始します + stripe.style.transitionDelay = '-' + sec + 's'; +*/!* + stripe.classList.add('animate'); +}; +``` + +## transition-timing-function + +タイミング関数はアニメーションプロセスが時間と共にどのように広がっていくかを記述します。ゆっくりと始まりその急速に進む、またはその逆もありえます。 + +これは一見すると最も複雑なプロパティです。しかし少し時間をかけて見れば、非常に簡単のものになります。 + +このプロパティは2種類の値を受け取ります: ベジェ曲線またはステップです。より頻繁に使われる曲線から見ていきましょう。 + +### ベジェ曲線 + +タイミング関数は次の条件を満たす4つの制御点をもつ [ベジェ曲線](/bezier-curve) として設定できます。: + +1. 最初の制御点: `(0,0)`. +2. 最後の制御点: `(1,1)`. +3. 中間点については、`x` の値は区間 `0..1` になければならず、`y` は何でも構いません。 + +CSS でのベジェ曲線の構文です: `cubic-bezier(x2, y2, x3, y3)`。 +ここでは 2番目と3番目の制御点だけを指定します。なぜなら、最初の点は `(0,0)` 固定であり、4番目は `(1,1)` 固定だからです。 + +タイミング関数は時間の中でアニメーション処理がどのような速さで進むかを記述します。 + +- `x` 軸は時間です: `0` -- は開始時点、`1` -- は `transition-duration` の最後の瞬間です。 +- `y` 軸は処理の完了を指定します: `0` -- はプロパティの開始値であり, `1` -- は終わりの値です。 + +最もシンプルなバリアントは、同じ線形の速度でアニメーションが均一に進む場合です。それは曲線 `cubic-bezier(0, 0, 1, 1)` として指定することができます。 + +これは、その曲線がどのように見えるかを示したものです: + +![](bezier-linear.svg) + +...ご覧の通り、単なる直線です。時間(`x`)が過ぎるに連れて、アニメーションの完了(`y`)は着実に `0` から `1` に進みます。 + +下の例にある電車は、左から右へ一定の速度で移動します(クリックしてみてください): + +[codetabs src="train-linear"] + +CSS `transition` はその曲線に基づいています: + +```css +.train { + left: 0; + transition: left 5s cubic-bezier(0, 0, 1, 1); + /* JavaScript sets left to 450px */ +} +``` + +...そして電車をスローダウンさせるにはどうすれば良いでしょうか? + +別のベジェ曲線を使うことで実現できます: `cubic-bezier(0.0, 0.5, 0.5 ,1.0)`. + +グラフは次のようになります: + +![](train-curve.svg) + +見てわかるように、処理は速く始まります: 曲線は高くなっていき、その後遅くなっていきます。 + +タイミング関数は次のように動作します(電車をクリックしてください): + +[codetabs src="train"] + +CSS: +```css +.train { + left: 0; + transition: left 5s cubic-bezier(0, .5, .5, 1); + /* JavaScript sets left to 450px */ +} +``` + +いくつかの組み込みの曲線があります: `linear`, `ease`, `ease-in`, `ease-out` そして `ease-in-out` です。 + +`linear` は `cubic-bezier(0, 0, 1, 1)` を簡略したものです -- それは直線であり、先程見たものです。 + +その他の名前は以下の `cubic-bezier` の簡略表記です: + +| <code>ease</code><sup>*</sup> | <code>ease-in</code> | <code>ease-out</code> | <code>ease-in-out</code> | +|-------------------------------|----------------------|-----------------------|--------------------------| +| <code>(0.25, 0.1, 0.25, 1.0)</code> | <code>(0.42, 0, 1.0, 1.0)</code> | <code>(0, 0, 0.58, 1.0)</code> | <code>(0.42, 0, 0.58, 1.0)</code> | +| ![ease, figure](ease.svg) | ![ease-in, figure](ease-in.svg) | ![ease-out, figure](ease-out.svg) | ![ease-in-out, figure](ease-in-out.svg) | + +`*` -- デフォルトでは、タイミング関数がない場合 `ease` が使用されます。 + +したがって、スローダウンする電車に対しては、`ease-out` を使うことができました。: + +```css +.train { + left: 0; + transition: left 5s ease-out; + /* transition: left 5s cubic-bezier(0, .5, .5, 1); */ +} +``` + +しかし、実際は少し異なって見えます。 + +**ベジェ曲線はアニメーションがその範囲から "飛び出す" ようにすることができます。** + +曲線上の制御点は、負または巨大な値の `y` 座標を持つことができます。すると、ベジェ曲線も非常に低くまたは高くジャンプし、アニメーションが通常の範囲を超えます。 + +下の例のアニメーションコードは次の通りです: +```css +.train { + left: 100px; + transition: left 5s cubic-bezier(.5, -1, .5, 2); + /* JavaScript sets left to 400px */ +} +``` + +プロパティ `left` は `100px` から `400px` までアニメーションするはずです。 + +しかし、電車をクリックすると、次のようになります: + +- まず、電車は *バック* します: `left` は `100px` よりも小さくなります。 +- 次に前に進み、`400px` よりも少し先に進みます。 +- その後再びバックし -- `400px` になります。 + +[codetabs src="train-over"] + +なぜこのようなことが起きるのでしょう? -- 与えられたベジェ曲線を見れば明らかです: + +![](bezier-train-over.svg) + +2番目の `y` 座標がゼロより下に移動し、3番目の点は `1` を越えています。そのため、曲線は "通常" の象限から外れています。`y` は "標準" の範囲 `0..1` から外れています。 + +ご存知の通り、`y` は "アニメーション処理の完了" を表します。値 `y = 0` は開始プロパティ値に対応し、`y = 1` は -- 終わりの値に対応します。そのため、値 `y<0` はプロパティを開始時の `left` よりも小さい値に移動させ、`y>1` は -- 最後の `left` を越えます。 + +これは確実に "ソフトな" バリアントです。もし `y` の値を `-99` や `99` といった値にした場合、電車はその範囲から遥か遠くに飛び出します。 + +しかし、特定のタスクのためのベジェ曲線はどうやって作るのでしょう?そのための多くのツールがあります。例えば、<http://cubic-bezier.com/> などで行うことができます。 + +### Steps + +タイミング関数 `steps(number of steps[, start/end])` はアニメーションをステップに分割することができます。 + +数字を使った例を見てみましょう。私たちは数字を滑らかではなく、離散的に変化させます。 + +そのために、アニメーションを 9 つのステップに分割します: + +```css +#stripe.animate { + transform: translate(-90%); + transition: transform 9s *!*steps(9, start)*/!*; +} +``` + +`steps(9, start)` の動作です: + +[codetabs src="step"] + +最初の `steps` の引数はステップの数です。変換は 9 つのパートに分割されます(それぞれ 10%)。時間間隔も同様に分割されます: 9秒は1秒間隔になります。 + +2つ目の引数は `start` または `end` いずれかの単語です。 + +`start` はアニメーション開始時にすぐに最初のステップを行うことを意味します。 + +アニメーションでそれが確認できます。数字をクリックすると、すぐに `1` (最初のステップ) に変わり、以降は次の秒のはじめに変化していきます。 + +プロセスはこのように処理されます: + +- `0s` -- `-10%` (1秒目の頭で最初の変更がされます, 開始直後) +- `1s` -- `-20%` +- ... +- `8s` -- `-80%` +- (最後の秒は最後の値を表示します)。 + +もう1つの値 `end` は、変化は各秒の最初ではなく最後に適用されるようにすることを意味します。 + +したがって、処理は次のように進みます: + +- `0s` -- `0` +- `1s` -- `-10%` (最初の変更は1秒目の最後です) +- `2s` -- `-20%` +- ... +- `9s` -- `-90%` + +`steps(9, end)` の動作です: + +[codetabs src="step-end"] + +簡略表記もあります: + +- `step-start` -- は `steps(1, start)` と同じです。つまり、アニメーションはすぐに始まり、ステップは1つです。なので、これは開始後すぐに終わり、まるでアニメーションがないかのように見えます。 +- `step-end` -- は `steps(1, end)` と同じです。: `transition-duration` の終わりに単一ステップのアニメーションを行います。 + +これらの値はほとんど使われません。なぜなら、実際にはアニメーションではなく、単なる単一ステップの変更だからです。 + +## transitionend イベント + +CSS アニメーションが終了すると、`transitionend` イベントがトリガされます。 + +これはアニメーション完了後になにかをするのに広く使われています。また、アニメーションを付け加える事もできます。 + +例えば、下の例にある船は、クリックで往復し始めます。時間が経つごとにどんどん右に行きます。 + +[iframe src="boat" height=300 edit link] + +アニメーションは関数 `go` によって開始され、遷移が終了して方向を反転する度に `go` が再実行されます: + +```js +boat.onclick = function() { + //... + let times = 1; + + function go() { + if (times % 2) { + // 右に進みます + boat.classList.remove('back'); + boat.style.marginLeft = 100 * times + 200 + 'px'; + } else { + // 左に進みます + boat.classList.add('back'); + boat.style.marginLeft = 100 * times - 200 + 'px'; + } + + } + + go(); + + boat.addEventListener('transitionend', function() { + times++; + go(); + }); +}; +``` + +`transitionend` のイベントオブジェクトはいくつかのプロパティを持っています。: + +`event.propertyName` +: アニメーションを終了したプロパティ。複数のプロパティを同時にアニメーションした場合に便利です。 + +`event.elapsedTime` +: `transition-delay` を除く、アニメーションの時間(秒単位)。 + +## キーフレーム(keyframes) + +`@keyframes` という CSS のルールを使用して、複数の簡単なアニメーションを一緒に動作させることができます。 + +この方法では、アニメーションの "名前" と、何を/いつ/どこでアニメーションさせるかのルールを指定します。その後、`animation` プロパティを使ってアニメーションと要素の紐づけを行い、追加のパラメータを指定していきます。 + +これは説明付きの例です: + +```html run height=60 autorun="no-epub" no-beautify +<div class="progress"></div> + +<style> +*!* + @keyframes go-left-right { /* 名前を指定します: "go-left-right" */ + from { left: 0px; } /* left: 0px からアニメーションを開始します */ + to { left: calc(100% - 50px); } /* left: 100%-50px までアニメーションします */ + } +*/!* + + .progress { +*!* + animation: go-left-right 3s infinite alternate; + /* アニメーション "go-left-right" を要素に適用します + 期間は 3 秒 (3s) + 回数: 無限 (infinite) + 順方向/逆方向を毎回交互に (alternate) + */ +*/!* + + position: relative; + border: 2px solid green; + width: 50px; + height: 20px; + background: lime; + } +</style> +``` + +`@keyframes` や [詳細な仕様](https://drafts.csswg.org/css-animations/) について多くの記事があります。 + +ただし、あなたのサイト上で常に動いているものがない限り、恐らく `@keyframes` を頻繁に必要とはしないでしょう。 + +## サマリ + +CSS アニメーションは、1つ以上の CSS プロパティの変更をなめらかにアニメーション化できます。 + +これはほとんどのアニメーションのタスクに適しています。なお、アニメーションに JavaScript を使うこともできます。次のチャプターではそれを見ていきます。 + +JavaScript アニメーションと比較した場合の CSS アニメーションの制限は次の通りです: + +```compare plus="CSS animations" minus="JavaScript animations" ++ 簡単なことは簡単にできます。 ++ CPU に対し高速であり軽量です。 +- JavaScript アニメーションは柔軟です。 要素の "爆発" のような任意のアニメーションロジックを実装することができます。 +- 単なるプロパティの変更ではありません。JavaScript ではアニメーション目的で新しい要素を作成すると言ったことが可能です。 +``` + +大部分のアニメーションはこのチャプターで説明した CSS を使用して実装することができます。そして `transitionend` イベントはアニメーションの後に JavaScript を実行することができるので、コードともうまく統合できます。 + +しかし、次のチャプターではより複雑なケースを取り扱うため、 JavaScript アニメーションをいくつか見ていきます。 diff --git a/7-animation/2-css-animations/bezier-linear.svg b/7-animation/2-css-animations/bezier-linear.svg new file mode 100644 index 0000000000..0c2e970f29 --- /dev/null +++ b/7-animation/2-css-animations/bezier-linear.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="144" height="150" viewBox="0 0 144 150"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="Group" transform="translate(12 13)"><path id="Shape" stroke="#A7333A" stroke-width="2" d="M4.17 103.348L104.314 3.505"/><circle id="Oval" cx="4" cy="104" r="4" fill="#FFF" stroke="#DBAF88"/><text id="1" fill="#AF6E24" fill-rule="nonzero" font-family="OpenSans-Bold, Open Sans" font-size="10.28" font-weight="bold"><tspan x="0" y="125">1</tspan></text><circle id="Oval" cx="104" cy="4" r="4" fill="#FFF" stroke="#DBAF88"/><text id="2" fill="#AF6E24" fill-rule="nonzero" font-family="OpenSans-Bold, Open Sans" font-size="10.28" font-weight="bold"><tspan x="100.101" y="25">2</tspan></text></g></g></svg> \ No newline at end of file diff --git a/7-animation/2-css-animations/bezier-train-over.svg b/7-animation/2-css-animations/bezier-train-over.svg new file mode 100644 index 0000000000..d12d092259 --- /dev/null +++ b/7-animation/2-css-animations/bezier-train-over.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="225" height="331" viewBox="0 0 225 331"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><defs><path id="path-1" d="M34 15h150v150H34z"/><mask id="mask-2" width="150" height="150" x="0" y="0" fill="#fff" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox"><use xlink:href="#path-1"/></mask></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="bezier-train-over.svg"><g id="Group" opacity=".7" transform="translate(0 74)"><use id="Rectangle-233" stroke="#7E7C7B" stroke-dasharray="3,2,3,2" stroke-width="2" mask="url(#mask-2)" xlink:href="#path-1"/><text id="(1,1)" fill="#7E7C7B" font-family="PTMono-Bold, PT Mono" font-size="12" font-weight="bold"><tspan x="189" y="11">(1,1)</tspan></text><text id="(0,0)" fill="#7E7C7B" font-family="PTMono-Bold, PT Mono" font-size="12" font-weight="bold"><tspan x="0" y="157">(0,0)</tspan></text><text id="(0,1)" fill="#7E7C7B" font-family="PTMono-Bold, PT Mono" font-size="12" font-weight="bold"><tspan x="0" y="11">(0,1)</tspan></text><text id="(1,0)" fill="#7E7C7B" font-family="PTMono-Bold, PT Mono" font-size="12" font-weight="bold"><tspan x="189" y="157">(1,0)</tspan></text></g><path id="Line-2" stroke="#DBAF88" stroke-linecap="square" d="M34.5 239.5l73 73"/><path id="bezier-path" stroke="#A7333A" stroke-width="2" d="M34 239c75 75 75-225 150-150"/><circle id="Oval-4" cx="34" cy="239" r="4" fill="#FFF" stroke="#DBAF88"/><text id="-2" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="30" y="261">1</tspan></text><circle id="Oval-6" cx="109" cy="314" r="4" fill="#FFF" stroke="#DBAF88"/><text id="-3" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="92" y="320">2</tspan></text><path id="Line" stroke="#DBAF88" stroke-linecap="square" d="M110.5 15.5l73 73"/><circle id="Oval-5" cx="184" cy="89" r="4" fill="#FFF" stroke="#DBAF88"/><text id="-5" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="188" y="107">4</tspan></text><circle id="Oval-7" cx="109" cy="14" r="4" fill="#FFF" stroke="#DBAF88"/><text id="-4" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="92" y="21">3</tspan></text></g></g></svg> \ No newline at end of file diff --git a/3-animation/2-css-animations/boat.view/index.html b/7-animation/2-css-animations/boat.view/index.html similarity index 100% rename from 3-animation/2-css-animations/boat.view/index.html rename to 7-animation/2-css-animations/boat.view/index.html diff --git a/3-animation/2-css-animations/boat.view/style.css b/7-animation/2-css-animations/boat.view/style.css similarity index 100% rename from 3-animation/2-css-animations/boat.view/style.css rename to 7-animation/2-css-animations/boat.view/style.css diff --git a/3-animation/2-css-animations/digits-negative-delay.view/index.html b/7-animation/2-css-animations/digits-negative-delay.view/index.html similarity index 100% rename from 3-animation/2-css-animations/digits-negative-delay.view/index.html rename to 7-animation/2-css-animations/digits-negative-delay.view/index.html diff --git a/3-animation/2-css-animations/digits-negative-delay.view/script.js b/7-animation/2-css-animations/digits-negative-delay.view/script.js similarity index 100% rename from 3-animation/2-css-animations/digits-negative-delay.view/script.js rename to 7-animation/2-css-animations/digits-negative-delay.view/script.js diff --git a/3-animation/2-css-animations/digits-negative-delay.view/style.css b/7-animation/2-css-animations/digits-negative-delay.view/style.css similarity index 100% rename from 3-animation/2-css-animations/digits-negative-delay.view/style.css rename to 7-animation/2-css-animations/digits-negative-delay.view/style.css diff --git a/3-animation/2-css-animations/digits.view/index.html b/7-animation/2-css-animations/digits.view/index.html similarity index 100% rename from 3-animation/2-css-animations/digits.view/index.html rename to 7-animation/2-css-animations/digits.view/index.html diff --git a/3-animation/2-css-animations/digits.view/script.js b/7-animation/2-css-animations/digits.view/script.js similarity index 100% rename from 3-animation/2-css-animations/digits.view/script.js rename to 7-animation/2-css-animations/digits.view/script.js diff --git a/3-animation/2-css-animations/digits.view/style.css b/7-animation/2-css-animations/digits.view/style.css similarity index 100% rename from 3-animation/2-css-animations/digits.view/style.css rename to 7-animation/2-css-animations/digits.view/style.css diff --git a/7-animation/2-css-animations/ease-in-out.svg b/7-animation/2-css-animations/ease-in-out.svg new file mode 100644 index 0000000000..d5c8809d81 --- /dev/null +++ b/7-animation/2-css-animations/ease-in-out.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="149" height="187" viewBox="0 0 149 187"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="ease-in-out.svg"><path id="Path-1" stroke="#A7333A" stroke-width="2" d="M23.688 143.905c42.32 0 58.041-100.414 100.467-100.414"/><path id="Line" stroke="#DBAF88" stroke-linecap="square" d="M24.5 143.5h42.297"/><path id="Line-2" stroke="#DBAF88" stroke-linecap="square" d="M83.5 43.5h42.297"/><circle id="Oval-1" cx="24" cy="144" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-2" cx="124" cy="43" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-3" cx="82" cy="43" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-4" cx="66" cy="143" r="4" fill="#FFF" stroke="#DBAF88"/><text id="1" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="20" y="165">1</tspan></text><text id="2" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="62" y="165">2</tspan></text><text id="3" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="78" y="35">3</tspan></text><text id="4" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="121" y="35">4</tspan></text></g></g></svg> \ No newline at end of file diff --git a/7-animation/2-css-animations/ease-in.svg b/7-animation/2-css-animations/ease-in.svg new file mode 100644 index 0000000000..38c98ecbc1 --- /dev/null +++ b/7-animation/2-css-animations/ease-in.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="149" height="187" viewBox="0 0 149 187"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="ease-in.svg"><path id="Path-1" stroke="#A7333A" stroke-width="2" d="M23.688 143.905c42.32 0 100.467-100.414 100.467-100.414"/><path id="Line" stroke="#DBAF88" stroke-linecap="square" d="M24.5 143.5h42.297"/><circle id="Oval-1" cx="24" cy="144" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-2" cx="124" cy="43" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-4" cx="66" cy="143" r="4" fill="#FFF" stroke="#DBAF88"/><text id="1" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="20" y="165">1</tspan></text><text id="2" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="62" y="165">2</tspan></text><text id="3" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="114" y="35">3</tspan></text><text id="4" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="127" y="35">4</tspan></text></g></g></svg> \ No newline at end of file diff --git a/7-animation/2-css-animations/ease-out.svg b/7-animation/2-css-animations/ease-out.svg new file mode 100644 index 0000000000..9d22eeafd5 --- /dev/null +++ b/7-animation/2-css-animations/ease-out.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="149" height="187" viewBox="0 0 149 187"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="ease-out.svg"><path id="Path-1" stroke="#A7333A" stroke-width="2" d="M23.688 143.905S81.885 43.49 124.155 43.49"/><path id="Line-2" stroke="#DBAF88" stroke-linecap="square" d="M83.5 43.5h40.3"/><circle id="Oval-1" cx="24" cy="144" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-2" cx="124" cy="43" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-3" cx="82" cy="43" r="4" fill="#FFF" stroke="#DBAF88"/><text id="1" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="13" y="165">1</tspan></text><text id="2" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="27" y="165">2</tspan></text><text id="3" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="79" y="35">3</tspan></text><text id="4" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="120" y="35">4</tspan></text></g></g></svg> \ No newline at end of file diff --git a/7-animation/2-css-animations/ease.svg b/7-animation/2-css-animations/ease.svg new file mode 100644 index 0000000000..8f9d41fe83 --- /dev/null +++ b/7-animation/2-css-animations/ease.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="149" height="187" viewBox="0 0 149 187"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="ease.svg"><path id="Path-1" stroke="#A7333A" stroke-width="2" d="M23.688 143.905c25.1-9.89 25.1-100.414 100.467-100.414"/><path id="Line" stroke="#DBAF88" stroke-linecap="square" d="M24.5 143.5l24-9"/><path id="Line-2" stroke="#DBAF88" stroke-linecap="square" d="M50.5 43.5h73.3"/><circle id="Oval-1" cx="24" cy="144" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-2" cx="124" cy="43" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-3" cx="49" cy="43" r="4" fill="#FFF" stroke="#DBAF88"/><circle id="Oval-4" cx="49" cy="134" r="4" fill="#FFF" stroke="#DBAF88"/><text id="1" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="20" y="165">1</tspan></text><text id="2" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="45" y="156">2</tspan></text><text id="3" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="45" y="35">3</tspan></text><text id="4" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="121" y="35">4</tspan></text></g></g></svg> \ No newline at end of file diff --git a/3-animation/2-css-animations/step-end.view/index.html b/7-animation/2-css-animations/step-end.view/index.html similarity index 100% rename from 3-animation/2-css-animations/step-end.view/index.html rename to 7-animation/2-css-animations/step-end.view/index.html diff --git a/3-animation/2-css-animations/step-end.view/style.css b/7-animation/2-css-animations/step-end.view/style.css similarity index 100% rename from 3-animation/2-css-animations/step-end.view/style.css rename to 7-animation/2-css-animations/step-end.view/style.css diff --git a/3-animation/2-css-animations/step.view/index.html b/7-animation/2-css-animations/step.view/index.html similarity index 100% rename from 3-animation/2-css-animations/step.view/index.html rename to 7-animation/2-css-animations/step.view/index.html diff --git a/3-animation/2-css-animations/step.view/style.css b/7-animation/2-css-animations/step.view/style.css similarity index 100% rename from 3-animation/2-css-animations/step.view/style.css rename to 7-animation/2-css-animations/step.view/style.css diff --git a/7-animation/2-css-animations/train-curve.svg b/7-animation/2-css-animations/train-curve.svg new file mode 100644 index 0000000000..298dacd4c3 --- /dev/null +++ b/7-animation/2-css-animations/train-curve.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="149" height="187" viewBox="0 0 149 187"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="train-curve.svg"><path id="Path-13" stroke="#A7333A" stroke-width="2" d="M26.137 142.908c0-49.408 50.364-99.664 100.358-99.664"/><path id="Line" stroke="#DBAF88" stroke-linecap="square" d="M25.5 143.5V93.34"/><circle id="Oval-4" cx="26" cy="144" r="4" fill="#FFF" stroke="#DBAF88"/><text id="-2" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="22" y="165">1</tspan></text><circle id="Oval-6" cx="26" cy="94" r="4" fill="#FFF" stroke="#DBAF88"/><text id="2" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="22" y="87">2</tspan></text><path id="Line" stroke="#DBAF88" stroke-linecap="square" d="M75.5 43.5h50.636"/><circle id="Oval-5" cx="126" cy="44" r="4" fill="#FFF" stroke="#DBAF88"/><text id="4" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="122" y="36">4</tspan></text><circle id="Oval-7" cx="76" cy="44" r="4" fill="#FFF" stroke="#DBAF88"/><text id="3" fill="#AF6E24" font-family="OpenSans-Bold, Open Sans" font-size="14" font-weight="bold"><tspan x="72" y="36">3</tspan></text></g></g></svg> \ No newline at end of file diff --git a/3-animation/2-css-animations/train-linear.view/index.html b/7-animation/2-css-animations/train-linear.view/index.html similarity index 100% rename from 3-animation/2-css-animations/train-linear.view/index.html rename to 7-animation/2-css-animations/train-linear.view/index.html diff --git a/3-animation/2-css-animations/train-linear.view/style.css b/7-animation/2-css-animations/train-linear.view/style.css similarity index 100% rename from 3-animation/2-css-animations/train-linear.view/style.css rename to 7-animation/2-css-animations/train-linear.view/style.css diff --git a/3-animation/2-css-animations/train-over.view/index.html b/7-animation/2-css-animations/train-over.view/index.html similarity index 100% rename from 3-animation/2-css-animations/train-over.view/index.html rename to 7-animation/2-css-animations/train-over.view/index.html diff --git a/3-animation/2-css-animations/train-over.view/style.css b/7-animation/2-css-animations/train-over.view/style.css similarity index 100% rename from 3-animation/2-css-animations/train-over.view/style.css rename to 7-animation/2-css-animations/train-over.view/style.css diff --git a/3-animation/2-css-animations/train.view/index.html b/7-animation/2-css-animations/train.view/index.html similarity index 100% rename from 3-animation/2-css-animations/train.view/index.html rename to 7-animation/2-css-animations/train.view/index.html diff --git a/3-animation/2-css-animations/train.view/style.css b/7-animation/2-css-animations/train.view/style.css similarity index 100% rename from 3-animation/2-css-animations/train.view/style.css rename to 7-animation/2-css-animations/train.view/style.css diff --git a/7-animation/3-js-animation/1-animate-ball/solution.md b/7-animation/3-js-animation/1-animate-ball/solution.md new file mode 100644 index 0000000000..4e2dfd8279 --- /dev/null +++ b/7-animation/3-js-animation/1-animate-ball/solution.md @@ -0,0 +1,21 @@ +バウンドさせるには、`position:relative` をもつフィールド内のボールに対して、CSS プロパティ `top` と `position:absolute` を使うことで実現できます。 + +フィールドの下部の座標は `field.clientHeight` です。しかし、`top` プロパティはボール上部の座標のため、下端の位置は `field.clientHeight - ball.clientHeight` になります。 + +したがって、`top` を `0` から `field.clientHeight - ball.clientHeight` までアニメートします。 + +あとは、"バウンド" 効果を行うためにタイミング関数 `bounce` を `easeOut` モードで使います。 + +これがアニメーションの最終的なコードです: + +```js +let to = field.clientHeight - ball.clientHeight; + +animate({ + duration: 2000, + timing: makeEaseOut(bounce), + draw(progress) { + ball.style.top = to * progress + 'px' + } +}); +``` diff --git a/3-animation/3-js-animation/1-animate-ball/solution.view/index.html b/7-animation/3-js-animation/1-animate-ball/solution.view/index.html similarity index 100% rename from 3-animation/3-js-animation/1-animate-ball/solution.view/index.html rename to 7-animation/3-js-animation/1-animate-ball/solution.view/index.html diff --git a/3-animation/3-js-animation/1-animate-ball/solution.view/style.css b/7-animation/3-js-animation/1-animate-ball/solution.view/style.css similarity index 100% rename from 3-animation/3-js-animation/1-animate-ball/solution.view/style.css rename to 7-animation/3-js-animation/1-animate-ball/solution.view/style.css diff --git a/3-animation/3-js-animation/1-animate-ball/source.view/index.html b/7-animation/3-js-animation/1-animate-ball/source.view/index.html similarity index 100% rename from 3-animation/3-js-animation/1-animate-ball/source.view/index.html rename to 7-animation/3-js-animation/1-animate-ball/source.view/index.html diff --git a/3-animation/3-js-animation/1-animate-ball/source.view/style.css b/7-animation/3-js-animation/1-animate-ball/source.view/style.css similarity index 100% rename from 3-animation/3-js-animation/1-animate-ball/source.view/style.css rename to 7-animation/3-js-animation/1-animate-ball/source.view/style.css diff --git a/7-animation/3-js-animation/1-animate-ball/task.md b/7-animation/3-js-animation/1-animate-ball/task.md new file mode 100644 index 0000000000..bb98a8063e --- /dev/null +++ b/7-animation/3-js-animation/1-animate-ball/task.md @@ -0,0 +1,9 @@ +importance: 5 + +--- + +# バウンドするボールのアニメーション + +バウンドするボールを作ってください。ボールをクリックすると動きを確認できます。: + +[iframe height=250 src="solution"] diff --git a/7-animation/3-js-animation/2-animate-ball-hops/solution.md b/7-animation/3-js-animation/2-animate-ball-hops/solution.md new file mode 100644 index 0000000000..eeb255b53f --- /dev/null +++ b/7-animation/3-js-animation/2-animate-ball-hops/solution.md @@ -0,0 +1,32 @@ +タスク <info:task/animate-ball> では、アニメーションするプロパティは1つだけでした。ここではもう1つ必要です。:`elem.style.left`. + +水平座標は別の法則("バウンド" ではなく、徐々にボールを右にシフトする)で変化します。 + +そのため、もう一つの `animate` を記述します。 + +時間関数として `linear` も使えますが、`makeEaseOut(quad)` などの方がはるかによく見えます。 + +コード: + +```js +let height = field.clientHeight - ball.clientHeight; +let width = 100; + +// animate top (bouncing) +animate({ + duration: 2000, + timing: makeEaseOut(bounce), + draw: function(progress) { + ball.style.top = height * progress + 'px' + } +}); + +// animate left (moving to the right) +animate({ + duration: 2000, + timing: makeEaseOut(quad), + draw: function(progress) { + ball.style.left = width * progress + "px" + } +}); +``` diff --git a/3-animation/3-js-animation/2-animate-ball-hops/solution.view/index.html b/7-animation/3-js-animation/2-animate-ball-hops/solution.view/index.html similarity index 100% rename from 3-animation/3-js-animation/2-animate-ball-hops/solution.view/index.html rename to 7-animation/3-js-animation/2-animate-ball-hops/solution.view/index.html diff --git a/3-animation/3-js-animation/2-animate-ball-hops/solution.view/style.css b/7-animation/3-js-animation/2-animate-ball-hops/solution.view/style.css similarity index 100% rename from 3-animation/3-js-animation/2-animate-ball-hops/solution.view/style.css rename to 7-animation/3-js-animation/2-animate-ball-hops/solution.view/style.css diff --git a/7-animation/3-js-animation/2-animate-ball-hops/task.md b/7-animation/3-js-animation/2-animate-ball-hops/task.md new file mode 100644 index 0000000000..8436e9889b --- /dev/null +++ b/7-animation/3-js-animation/2-animate-ball-hops/task.md @@ -0,0 +1,13 @@ +importance: 5 + +--- + +# 右にバウンドするボールのアニメーション + +次のように、ボールを右にバウンドさせます。: + +[iframe height=250 src="solution"] + +アニメーションコードを記述してください。右への距離は `100px` です。 + +前のタスク <info:task/animate-ball> の答えをソースとして使ってください。 diff --git a/7-animation/3-js-animation/article.md b/7-animation/3-js-animation/article.md new file mode 100644 index 0000000000..9f67c05c0a --- /dev/null +++ b/7-animation/3-js-animation/article.md @@ -0,0 +1,458 @@ +# JavaScript アニメーション + +JavaScript アニメーションは CSS ではできないことを扱うことができます。 + +例えば、ベジェ曲線とは異なるタイミング関数を用いて複雑な経路に沿って移動したり、canvas 上でのアニメーションなどです。 + +[cut] + +## setInterval + +HTML/CSS の観点からは、アニメーションはスタイルプロパティの段階的な変更です。例えば、`style.left` を `0px` から `100px` に変更すると、要素が移動します。 + +そして、もしそれを `setInterval` の中で増加させるとき、毎秒 50 回の小さな変更を加えることによって、その変化はなめらかに見えます。これは映画館と同じ原理です。: 毎秒 24 以上のフレームがあれば十分に滑らかに見えます。 + +疑似コードは次のようになります: + +```js +let delay = 1000 / 50; // 1 秒で 50 フレーム +let timer = setInterval(function() { + if (animation complete) clearInterval(timer); + else increase style.left +}, delay) +``` + +より複雑なアニメーションの例: + +```js +let start = Date.now(); // 開始時間を覚える + +let timer = setInterval(function() { + // 開始からの経過時間は? + let timePassed = Date.now() - start; + + if (timePassed >= 2000) { + clearInterval(timer); // 2秒後にアニメーションが終了 + return; + } + + // timePassed 時点のアニメーションを描画 + draw(timePassed); + +}, 20); + +// timePassed は 0 から 2000 まで進む +// なので、left は 0px から 400px になります +function draw(timePassed) { + train.style.left = timePassed / 5 + 'px'; +} +``` + +デモです。電車をクリックしてみてください: + +[codetabs height=200 src="move"] + +## requestAnimationFrame + +複数のアニメーションが同時に実行されているとしましょう。 + +もしそれらを別々に実行し、それぞれが個別に `setInterval(..., 20)` を持っていると、ブラウザは `20ms` 間隔よりもっと頻繁に再描画をする必要があります。 + +各 `setInterval` は `20ms` 毎に一回トリガしますが、独立しているので `20ms` の中に複数の独立した実行があることになります。 + +これらの複数の独立した再描画は、ブラウザの再描画を簡単にし、CPUの負荷を減らしてよりなめらかに見せるためにグループ化すべきです。 + +言い換えると、次のコード: + +```js +setInterval(function() { + animate1(); + animate2(); + animate3(); +}, 20) +``` + +...は以下のコードよりも軽量です: + +```js +setInterval(animate1, 20); +setInterval(animate2, 20); +setInterval(animate3, 20); +``` + +心に留めておくべきことがもう一つあります。CPUが過負荷になっている場合や、その他再描画をあまりしなくて良い場合があります(ブラウザタブが非表示になっているようなとき)。そのため、本当は20ms毎に実行すべきではありません。 + +しかし、それを JavaScript ではどうやってしるのでしょう? 関数 `requestAnimationFrame` を提供する標準の [アニメーションタイミング](http://www.w3.org/TR/animation-timing/) があります。この関数は、これらすべての問題及び、その他多くのことに対応しています。 + +構文: +```js +let requestId = requestAnimationFrame(callback) +``` + +これは、ブラウザがアニメーションをしたい最も近い時間に `callback` 関数を実行するようスケジューリングします。 + +もし `callback` の中で要素を変更すると、他の `requestAnimationFrame` コールバックや CSS アニメーションと一緒にグループ化されます。これにより、配置の再計算と再描画がそれぞれではなく1回でまとめて行われます。 + +返却値 `requestId` は呼び出しをキャンセルするのに使うことができます: +```js +// スケジューリングされたコールバックの実行をキャンセルする +cancelAnimationFrame(requestId); +``` + +`callback` は1つの引数を取ります -- ページロードの開始からの経過時間のマイクロ秒です。 +この時間は [performance.now()](mdn:api/Performance/now) を呼び出すことでも得ることができます。 + +通常 `callback` は CPU が過負荷状態になったり、ノートPCのバッテリーがほとんどなかったり、その他別の理由がある場合を除きすぐに実行されます。 + +下のコードは `requestAnimationFrame` での最初の10回の実行時間を表示します。通常は 10-20ms です。 + +```html run height=40 refresh +<script> + let prev = performance.now(); + let times = 0; + + requestAnimationFrame(function measure(time) { + document.body.insertAdjacentHTML("beforeEnd", Math.floor(time - prev) + " "); + prev = time; + + if (times++ < 10) requestAnimationFrame(measure); + }) +</script> +``` + +## Structured animation + +これで、`requestAnimationFrame` に基づいた、様々な状況に対応することのできるアニメーション関数を作成することができます。 + +```js +function animate({timing, draw, duration}) { + + let start = performance.now(); + + requestAnimationFrame(function animate(time) { + // timeFraction は 0 から 1 になります + let timeFraction = (time - start) / duration; + if (timeFraction > 1) timeFraction = 1; + + // 現在のアニメーションの状態を計算します + let progress = timing(timeFraction) + + draw(progress); // 描画します + + if (timeFraction < 1) { + requestAnimationFrame(animate); + } + + }); +} +``` + +関数 `animate` はアニメーションを記述するための3つのパラメータを受け付けます。: + +`duration` +: アニメーションのトータルの時間。例: `1000`。 + +`timing(timeFraction)` +: 経過時間(開始時: `0`, 終了時: `1`)を基に、アニメーションの完了(ベジェ曲線の `y` のような) を返す、CSS プロパティ `transition-timing-function` のようなタイミング関数です。 + + 例えば、線形関数はアニメーションが同じスピードで均一に進むことを意味します。: + + ```js + function linear(timeFraction) { + return timeFraction; + } + ``` + + グラフはこのようになります: + ![](linear.svg) + + これは `transition-timing-function: linear` のようなものです。下にあるようなより興味深いケースがあります。 + +`draw(progress)` +: アニメーションの完了状態を取り、描画を行う関数です。値 `progress=0` はアニメーションの開始状態を示し、`progress=1` は終了状態を示します。 + + これは実際にアニメーションを描画する関数です。 + + 要素が移動します: + ```js + function draw(progress) { + train.style.left = progress + 'px'; + } + ``` + + ...または、他のことを行うことで、どんな方法でも何でもアニメーションさせることができます。 + +この関数を使って、要素の `width` を `0` から `100%` までアニメーションさせてみましょう。 + +デモ内の要素をクリックしてください: + +[codetabs height=60 src="width"] + +コードは次の通りです: + +```js +animate({ + duration: 1000, + timing(timeFraction) { + return timeFraction; + }, + draw(progress) { + elem.style.width = progress * 100 + '%'; + } +}); +``` + +CSS アニメーションとは異なり、任意のタイミング関数や描画関数を作ることができます。タイミング関数はベジェ曲線には制限されません。そして `draw` はプロパティを超えて、花火のアニメーションといった新しい要素を作成することもできます。 + +## タイミング関数 + +上記で最もシンプルな線形のタイミング関数を見ました。 + +他のものも見てみましょう。様々なタイミング関数でのアニメーションを試して、どのように動くのかを確認してみます。 + +### Power of n(n のべき乗) + +アニメーションをスピードアップさせたい場合には、`n` のべき乗で `progress` を使います。 + +例えば、放物曲線: + +```js +function quad(timeFraction) { + return Math.pow(timeFraction, 2) +} +``` + +グラフ: + +![](quad.svg) + +動作を見る(クリックして有効化): + +[iframe height=40 src="quad" link] + +...または、3次曲線のような `n` がより大きい場合。`n` を増やすことでより速度が上がります。 + +これは、べき乗 `5` での `progress` のグラフです。: + +![](quint.svg) + +動作を見る: + +[iframe height=40 src="quint" link] + +### 円弧 + +関数: + +```js +function circ(timeFraction) { + return 1 - Math.sin(Math.acos(timeFraction)); +} +``` + +グラフ: + +![](circ.svg) + +[iframe height=40 src="circ" link] + +### 戻る: 弓 + +この関数は "弓の射撃" を行います。最初に "弦を引き"、次に "撃ちます"。 + +前の関数とは異なり、追加のパラメータ `x`, "弾性係数" に依存します。"弦を引く" 距離はこれにより定義されます。 + +コード: + +```js +function back(x, timeFraction) { + return Math.pow(timeFraction, 2) * ((x + 1) * timeFraction - x) +} +``` + +**`x = 1.5` の場合のグラフ:** + +![](back.svg) + +アニメーションの場合、特定の `x` の値で使用します。これは `x = 1.5` の例です: + +[iframe height=40 src="back" link] + +### バウンド + +ボールを落としたと想像してください。それは落ちて後何度か跳ね返ってから停止します。 + +`bounce` 関数はそれと同じことをしますが、始まる順序は逆です。なお、このために必要な特別な係数はほとんどありません。: + +```js +function bounce(timeFraction) { + for (let a = 0, b = 1, result; 1; a += b, b /= 2) { + if (timeFraction >= (7 - 4 * a) / 11) { + return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2) + } + } +} +``` + +動作を見る: + +[iframe height=40 src="bounce" link] + +### 弾性のあるアニメーション + +"初期範囲" 用の追加パラメータ `x` を受け取るもう一つの "弾む" 関数です。 + +```js +function elastic(x, timeFraction) { + return Math.pow(2, 10 * (timeFraction - 1)) * Math.cos(20 * Math.PI * x / 3 * timeFraction) +} +``` + +**`x=1.5` のグラフです:** +![](elastic.svg) + +`x=1.5` の場合の動作: + +[iframe height=40 src="elastic" link] + +## Reversal: ease* + +ここまでで様々なタイミング関数があります。これらは "easeIn" と呼ばれます。 + +アニメーションを逆の順序で表示する必要があることがあります。これは、"easeOut" 変換で行います。 + +### easeOut + +"easeOut" モードでは、`timing` 関数はラッパー `timingEaseOut` の中に配置されます。 + +```js +timingEaseOut(timeFraction) = 1 - timing(1 - timeFraction) +``` + +つまり、"通常の" タイミング関数を取り、"そのラッパーを返す" "変換" 関数 `makeEaseOut` を使用します。: + +```js +// タイミング関数を引数とし、変換したものを返す +function makeEaseOut(timing) { + return function(timeFraction) { + return 1 - timing(1 - timeFraction); + } +} +``` + +例えば、上述の `bounce` 関数に対して適用してみます: + +```js +let bounceEaseOut = makeEaseOut(bounce); +``` + +すると、最初ではなくアニメーションの最後にバウンドするようになります。より自然にみえます。: + +[codetabs src="bounce-easeout"] + +ここでは、変換関数がどのように元の関数の挙動を変化させたのかが確認できます: + +![](bounce-inout.svg) + +跳ね返るような、アニメーションが最初にある場合には、それは最後似表示されます。 + +上のグラフでは <span style="color:#EE6B47">通常のバウンド</span> は赤色、<span style="color:#62C0DC">easeOut のバウンド</span> は青色です。 + +- 通常のバウンド: 物体は下の方で跳ね、最後に急激に跳ね上がります。 +- `easeOut` : 最初に上に大きく跳ねてからバウンドします + +### easeInOut + +アニメーションの最初と最後両方でこの効果を見せることもできます。このトランジションは "easeInOut" と呼ばれます。 + +タイミング関数が与えられると、次のようにアニメーションの状態を算出します: + +```js +if (timeFraction <= 0.5) { // アニメーションの前半 + return timing(2 * timeFraction) / 2; +} else { // アニメーションの後半 + return (2 - timing(2 * (1 - timeFraction))) / 2; +} +``` + +このラッパーコードです: + +```js +function makeEaseInOut(timing) { + return function(timeFraction) { + if (timeFraction < .5) + return timing(2 * timeFraction) / 2; + else + return (2 - timing(2 * (1 - timeFraction))) / 2; + } +} + +bounceEaseInOut = makeEaseInOut(bounce); +``` + +`bounceEaseInOut` の動作を見る: + +[codetabs src="bounce-easeinout"] + +"easeInOut" 変換は2つのグラフを1つにします: アニメーションの前半用の `easeIn` と、後半用の `easeOut` (`easeIn` の反転)です。 + +円弧 `circ` タイミング関数を例にして、その `easeIn`, `easeOut` と `easeInOut` のグラフを比べると、その効果ががはっきりと分かります。: + +![](circ-ease.svg) + +- <span style="color:#EE6B47">赤</span> 通常の `circ` (`easeIn`). +- <span style="color:#8DB173">緑</span> -- `easeOut`. +- <span style="color:#62C0DC">青</span> -- `easeInOut`. + +ご覧の通り、アニメーションの前半のグラフは縮小された `easeIn` であり、後半は縮小された `easeOut` のグラフです。結果、アニメーションはそれぞれの効果ではじまり、そして終わります。 + +## より興味深い "draw" + +要素を移動させる代わりに、他のことをすることもできます。必要なことは適切な `draw` を記述することです。 + +これは "バウンド" するテキスト入力のアニメーション例です: + +[codetabs src="text"] + +## サマリ + +CSS では上手く扱えなかったり、厳密な制御が必要なアニメーションの場合、JavaScript が役立ちます。JavaScript アニメーションは `requestAnimationFrame` 経由で実装します。この組み込みのメソッドにより、ブラウザが再描画を準備するときに実行されるコールバック関数をセットアップすることができます。通常、それはすぐですが、正確な時間はブラウザに依存します。 + +また、これはページがバックグラウンドのときは再描画はまったく行いません。コールバックが実行されないからです。アニメーションは一時停止し、リソースも消費されません。これは素晴らしいことです。 + +これは、ほとんどのアニメーションのセットアップに使えるヘルパー関数 `animate` です: + +```js +function animate({timing, draw, duration}) { + + let start = performance.now(); + + requestAnimationFrame(function animate(time) { + // timeFraction は 0 tから 1 + let timeFraction = (time - start) / duration; + if (timeFraction > 1) timeFraction = 1; + + // 現在のアニメーションの状態を計算 + let progress = timing(timeFraction); + + draw(progress); // 描画 + + if (timeFraction < 1) { + requestAnimationFrame(animate); + } + + }); +} +``` + +オプション: + +- `duration` -- アニメーションの合計時間(ms)。 +- `timing` -- アニメーションの進行状況を計算する関数。0 〜 1 まで値を引数に取り、通常は 0 〜 1 でアニメーションの進行状況を返します。 +- `draw` -- アニメーションを描画する関数です。 + +もちろん、これを改善して様々なオプションを追加することができますが、JavaScript アニメーションは日常的に使用されるものではありません。これらはなにか興味深いことをする場合や非標準的なことをする際に利用されます。そのため、必要なときに必要な機能を追加するのがよいでしょう。 + +JavaScript アニメーションは、任意のタイミング関数を扱うことができます。ここでは多くの例を取り上げました。CSS とは異なり、JavaScript アニメーションはベジェ曲線に制限されません。 + +`draw` についても同様です。CSS プロパティだけでなく、何でもアニメーションにすることができます。 \ No newline at end of file diff --git a/7-animation/3-js-animation/back.svg b/7-animation/3-js-animation/back.svg new file mode 100644 index 0000000000..fcef09ad75 --- /dev/null +++ b/7-animation/3-js-animation/back.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="234" height="240" viewBox="0 0 234 240"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="back.svg"><path id="Line" fill="#DBAF88" fill-rule="nonzero" d="M34 22.16l7 14h-6V210.5h-2V36.16h-6l7-14z"/><path id="Line" fill="#DBAF88" fill-rule="nonzero" d="M194.51 187l14 7-14 7-.001-6H23.5v-2h171.009l.001-6z"/><path id="Path-747" stroke="#C06334" stroke-width="3" d="M35.329 193.636s47.217 15.849 69.738 15.008c22.521-.842 61.26 6.27 103.399-186.198"/><text id="0" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="17" y="212">0</tspan></text><text id="1" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="17" y="36">1</tspan></text><text id="-2" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="200" y="212">1</tspan></text></g></g></svg> \ No newline at end of file diff --git a/3-animation/3-js-animation/back.view/index.html b/7-animation/3-js-animation/back.view/index.html similarity index 100% rename from 3-animation/3-js-animation/back.view/index.html rename to 7-animation/3-js-animation/back.view/index.html diff --git a/3-animation/3-js-animation/back.view/style.css b/7-animation/3-js-animation/back.view/style.css similarity index 100% rename from 3-animation/3-js-animation/back.view/style.css rename to 7-animation/3-js-animation/back.view/style.css diff --git a/7-animation/3-js-animation/bezier-linear.svg b/7-animation/3-js-animation/bezier-linear.svg new file mode 100644 index 0000000000..0c2e970f29 --- /dev/null +++ b/7-animation/3-js-animation/bezier-linear.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="144" height="150" viewBox="0 0 144 150"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="Group" transform="translate(12 13)"><path id="Shape" stroke="#A7333A" stroke-width="2" d="M4.17 103.348L104.314 3.505"/><circle id="Oval" cx="4" cy="104" r="4" fill="#FFF" stroke="#DBAF88"/><text id="1" fill="#AF6E24" fill-rule="nonzero" font-family="OpenSans-Bold, Open Sans" font-size="10.28" font-weight="bold"><tspan x="0" y="125">1</tspan></text><circle id="Oval" cx="104" cy="4" r="4" fill="#FFF" stroke="#DBAF88"/><text id="2" fill="#AF6E24" fill-rule="nonzero" font-family="OpenSans-Bold, Open Sans" font-size="10.28" font-weight="bold"><tspan x="100.101" y="25">2</tspan></text></g></g></svg> \ No newline at end of file diff --git a/3-animation/3-js-animation/bounce-easeinout.view/index.html b/7-animation/3-js-animation/bounce-easeinout.view/index.html similarity index 100% rename from 3-animation/3-js-animation/bounce-easeinout.view/index.html rename to 7-animation/3-js-animation/bounce-easeinout.view/index.html diff --git a/3-animation/3-js-animation/bounce-easeinout.view/style.css b/7-animation/3-js-animation/bounce-easeinout.view/style.css similarity index 100% rename from 3-animation/3-js-animation/bounce-easeinout.view/style.css rename to 7-animation/3-js-animation/bounce-easeinout.view/style.css diff --git a/3-animation/3-js-animation/bounce-easeout.view/index.html b/7-animation/3-js-animation/bounce-easeout.view/index.html similarity index 100% rename from 3-animation/3-js-animation/bounce-easeout.view/index.html rename to 7-animation/3-js-animation/bounce-easeout.view/index.html diff --git a/3-animation/3-js-animation/bounce-easeout.view/style.css b/7-animation/3-js-animation/bounce-easeout.view/style.css similarity index 100% rename from 3-animation/3-js-animation/bounce-easeout.view/style.css rename to 7-animation/3-js-animation/bounce-easeout.view/style.css diff --git a/7-animation/3-js-animation/bounce-inout.svg b/7-animation/3-js-animation/bounce-inout.svg new file mode 100644 index 0000000000..363633abd4 --- /dev/null +++ b/7-animation/3-js-animation/bounce-inout.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="234" height="240" viewBox="0 0 234 240"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="bounce-inout.svg"><path id="Line" fill="#DBAF88" fill-rule="nonzero" d="M34 22.16l7 14h-6V210.5h-2V36.16h-6l7-14z"/><path id="Line" fill="#DBAF88" fill-rule="nonzero" d="M194.51 187l14 7-14 7-.001-6H23.5v-2h171.009l.001-6z"/><path id="Path-747" stroke="#C06334" stroke-linejoin="bevel" stroke-width="3" d="M35.329 193.636s5.904-6.54 16.904 0c3.208-9.976 18.818-15.529 28.536 0 8.264-26.015 36.231-83.184 63.413.916 3.882-17.966 30.855-183.91 64.284-172.106"/><text id="0" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="17" y="212">0</tspan></text><text id="1" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="17" y="36">1</tspan></text><text id="-2" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="200" y="212">1</tspan></text><path id="Path-770" stroke="#1C85B5" stroke-linejoin="bevel" stroke-width="3" d="M35.484 192.777c17.973-6.6 33.865-22.649 61.654-174.279 4.913 13.935 14.875 44.582 29.92 45.499 15.047.917 29.503-32.95 33.164-43.662 6.397 7.861 4.89 11.09 14.736 11.09 9.847 0 15.39-9.552 16.623-10.805 2.205 1.977 10.596 3.784 15.379.83"/></g></g></svg> \ No newline at end of file diff --git a/3-animation/3-js-animation/bounce.view/index.html b/7-animation/3-js-animation/bounce.view/index.html similarity index 100% rename from 3-animation/3-js-animation/bounce.view/index.html rename to 7-animation/3-js-animation/bounce.view/index.html diff --git a/3-animation/3-js-animation/bounce.view/style.css b/7-animation/3-js-animation/bounce.view/style.css similarity index 100% rename from 3-animation/3-js-animation/bounce.view/style.css rename to 7-animation/3-js-animation/bounce.view/style.css diff --git a/7-animation/3-js-animation/circ-ease.svg b/7-animation/3-js-animation/circ-ease.svg new file mode 100644 index 0000000000..a7db9abcf7 --- /dev/null +++ b/7-animation/3-js-animation/circ-ease.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="234" height="240" viewBox="0 0 234 240"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="circ-ease.svg"><path id="Line" fill="#DBAF88" fill-rule="nonzero" d="M34 22.16l7 14h-6V210.5h-2V36.16h-6l7-14z"/><path id="Line" fill="#DBAF88" fill-rule="nonzero" d="M194.51 187l14 7-14 7-.001-6H23.5v-2h171.009l.001-6z"/><path id="Path-747" stroke="#C06334" stroke-linejoin="bevel" stroke-width="3" d="M35.16 193.735c81.198 0 165.415-52.198 173.306-171.29"/><path id="Path-748" stroke="#91C2A3" stroke-linejoin="bevel" stroke-width="3" d="M208.466 22.446c-81.198 0-165.415 52.197-173.306 171.289"/><text id="0" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="17" y="212">0</tspan></text><text id="1" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="17" y="36">1</tspan></text><text id="-2" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="200" y="212">1</tspan></text><path id="Path-770" stroke="#1C85B5" stroke-linejoin="bevel" stroke-width="3" d="M35.484 192.777c17.973-6.6 76.569 2.817 85.738-83.319 9.17-86.136 80.955-85.054 85.738-88.007"/></g></g></svg> \ No newline at end of file diff --git a/7-animation/3-js-animation/circ.svg b/7-animation/3-js-animation/circ.svg new file mode 100644 index 0000000000..3595dd6248 --- /dev/null +++ b/7-animation/3-js-animation/circ.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="234" height="240" viewBox="0 0 234 240"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="circ.svg"><path id="Line" fill="#DBAF88" fill-rule="nonzero" d="M34 22.16l7 14h-6V210.5h-2V36.16h-6l7-14z"/><path id="Line" fill="#DBAF88" fill-rule="nonzero" d="M194.51 187l14 7-14 7-.001-6H23.5v-2h171.009l.001-6z"/><path id="Path-747" stroke="#C06334" stroke-width="3" d="M35.329 193.636s154.546 3.372 173.137-171.19"/><text id="0" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="17" y="212">0</tspan></text><text id="1" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="17" y="36">1</tspan></text><text id="-2" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="200" y="212">1</tspan></text></g></g></svg> \ No newline at end of file diff --git a/3-animation/3-js-animation/circ.view/index.html b/7-animation/3-js-animation/circ.view/index.html similarity index 100% rename from 3-animation/3-js-animation/circ.view/index.html rename to 7-animation/3-js-animation/circ.view/index.html diff --git a/3-animation/3-js-animation/circ.view/style.css b/7-animation/3-js-animation/circ.view/style.css similarity index 100% rename from 3-animation/3-js-animation/circ.view/style.css rename to 7-animation/3-js-animation/circ.view/style.css diff --git a/7-animation/3-js-animation/elastic.svg b/7-animation/3-js-animation/elastic.svg new file mode 100644 index 0000000000..17f04ccde7 --- /dev/null +++ b/7-animation/3-js-animation/elastic.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="233" height="388" viewBox="0 0 233 388"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="elastic.svg"><path id="Line" fill="#DBAF88" fill-rule="nonzero" d="M34 22.16l7 14h-6V366h-2V36.16h-6l7-14z"/><path id="Line" fill="#DBAF88" fill-rule="nonzero" d="M194.51 187l14 7-14 7-.001-6H23.5v-2h171.009l.001-6z"/><path id="Path-747" stroke="#C06334" stroke-width="3" d="M34.88 192.698c3.442 0 11.656-1.502 18.614.34 8.527 2.258 8.021-3.415 33.52 1.301 13.1 2.423 13.41-10.73 28.147.561 20.158 15.445 18.716-35.537 33.359-.222 3.42 8.249 6.879 22.601 10.133 19.609 2.91-2.676 5.195-19.38 14.577-64.112 4.611-21.984 15.003 146.122 19.117 130.36 9.62-36.844 11.209-241.394 14.99-258.441"/><text id="0" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="17" y="212">0</tspan></text><text id="1" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="17" y="36">1</tspan></text><text id="-2" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="200" y="212">1</tspan></text></g></g></svg> \ No newline at end of file diff --git a/3-animation/3-js-animation/elastic.view/index.html b/7-animation/3-js-animation/elastic.view/index.html similarity index 100% rename from 3-animation/3-js-animation/elastic.view/index.html rename to 7-animation/3-js-animation/elastic.view/index.html diff --git a/3-animation/3-js-animation/elastic.view/style.css b/7-animation/3-js-animation/elastic.view/style.css similarity index 100% rename from 3-animation/3-js-animation/elastic.view/style.css rename to 7-animation/3-js-animation/elastic.view/style.css diff --git a/7-animation/3-js-animation/linear.svg b/7-animation/3-js-animation/linear.svg new file mode 100644 index 0000000000..daa753f0c2 --- /dev/null +++ b/7-animation/3-js-animation/linear.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="234" height="240" viewBox="0 0 234 240"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="linear.svg"><path id="Line" fill="#DBAF88" fill-rule="nonzero" d="M34 22.16l7 14h-6V210.5h-2V36.16h-6l7-14z"/><path id="Line" fill="#DBAF88" fill-rule="nonzero" d="M194.51 187l14 7-14 7-.001-6H23.5v-2h171.009l.001-6z"/><path id="Path-747" stroke="#C06334" stroke-width="3" d="M35.329 193.636L207.337 22.094"/><text id="0" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="17" y="212">0</tspan></text><text id="1" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="17" y="36">1</tspan></text><text id="-2" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="200" y="212">1</tspan></text></g></g></svg> \ No newline at end of file diff --git a/3-animation/3-js-animation/move-raf.view/index.html b/7-animation/3-js-animation/move-raf.view/index.html similarity index 100% rename from 3-animation/3-js-animation/move-raf.view/index.html rename to 7-animation/3-js-animation/move-raf.view/index.html diff --git a/3-animation/3-js-animation/move.view/index.html b/7-animation/3-js-animation/move.view/index.html similarity index 100% rename from 3-animation/3-js-animation/move.view/index.html rename to 7-animation/3-js-animation/move.view/index.html diff --git a/7-animation/3-js-animation/quad.svg b/7-animation/3-js-animation/quad.svg new file mode 100644 index 0000000000..25a4d00053 --- /dev/null +++ b/7-animation/3-js-animation/quad.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="234" height="240" viewBox="0 0 234 240"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="quad.svg"><path id="Line" fill="#DBAF88" fill-rule="nonzero" d="M34 22.16l7 14h-6V210.5h-2V36.16h-6l7-14z"/><path id="Line" fill="#DBAF88" fill-rule="nonzero" d="M194.51 187l14 7-14 7-.001-6H23.5v-2h171.009l.001-6z"/><path id="Path-747" stroke="#C06334" stroke-width="3" d="M35.329 193.636s78.474 15.205 172.008-171.542"/><text id="0" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="17" y="212">0</tspan></text><text id="1" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="17" y="36">1</tspan></text><text id="-2" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="200" y="212">1</tspan></text></g></g></svg> \ No newline at end of file diff --git a/3-animation/3-js-animation/quad.view/index.html b/7-animation/3-js-animation/quad.view/index.html similarity index 100% rename from 3-animation/3-js-animation/quad.view/index.html rename to 7-animation/3-js-animation/quad.view/index.html diff --git a/3-animation/3-js-animation/quad.view/style.css b/7-animation/3-js-animation/quad.view/style.css similarity index 100% rename from 3-animation/3-js-animation/quad.view/style.css rename to 7-animation/3-js-animation/quad.view/style.css diff --git a/7-animation/3-js-animation/quint.svg b/7-animation/3-js-animation/quint.svg new file mode 100644 index 0000000000..c879ef9318 --- /dev/null +++ b/7-animation/3-js-animation/quint.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="234" height="240" viewBox="0 0 234 240"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="animation" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="quint.svg"><path id="Line" fill="#DBAF88" fill-rule="nonzero" d="M34 22.16l7 14h-6V210.5h-2V36.16h-6l7-14z"/><path id="Line" fill="#DBAF88" fill-rule="nonzero" d="M194.51 187l14 7-14 7-.001-6H23.5v-2h171.009l.001-6z"/><path id="Path-747" stroke="#C06334" stroke-width="3" d="M35.329 193.636c114.13 3.248 136.033-9.361 172.008-171.542"/><text id="0" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="17" y="212">0</tspan></text><text id="1" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="17" y="36">1</tspan></text><text id="-2" fill="#AF6E24" font-family="PTMono-Bold, PT Mono" font-size="14" font-weight="bold"><tspan x="200" y="212">1</tspan></text></g></g></svg> \ No newline at end of file diff --git a/3-animation/3-js-animation/quint.view/index.html b/7-animation/3-js-animation/quint.view/index.html similarity index 100% rename from 3-animation/3-js-animation/quint.view/index.html rename to 7-animation/3-js-animation/quint.view/index.html diff --git a/3-animation/3-js-animation/quint.view/style.css b/7-animation/3-js-animation/quint.view/style.css similarity index 100% rename from 3-animation/3-js-animation/quint.view/style.css rename to 7-animation/3-js-animation/quint.view/style.css diff --git a/3-animation/3-js-animation/text.view/index.html b/7-animation/3-js-animation/text.view/index.html similarity index 100% rename from 3-animation/3-js-animation/text.view/index.html rename to 7-animation/3-js-animation/text.view/index.html diff --git a/3-animation/3-js-animation/text.view/style.css b/7-animation/3-js-animation/text.view/style.css similarity index 100% rename from 3-animation/3-js-animation/text.view/style.css rename to 7-animation/3-js-animation/text.view/style.css diff --git a/3-animation/3-js-animation/width.view/animate.js b/7-animation/3-js-animation/width.view/animate.js similarity index 100% rename from 3-animation/3-js-animation/width.view/animate.js rename to 7-animation/3-js-animation/width.view/animate.js diff --git a/3-animation/3-js-animation/width.view/index.html b/7-animation/3-js-animation/width.view/index.html similarity index 100% rename from 3-animation/3-js-animation/width.view/index.html rename to 7-animation/3-js-animation/width.view/index.html diff --git a/3-animation/index.md b/7-animation/index.md similarity index 100% rename from 3-animation/index.md rename to 7-animation/index.md diff --git a/7-network/1-xmlhttprequest/article.md b/7-network/1-xmlhttprequest/article.md deleted file mode 100644 index 105cc39b57..0000000000 --- a/7-network/1-xmlhttprequest/article.md +++ /dev/null @@ -1,440 +0,0 @@ -# XMLHttpRequest and AJAX - -`XMLHttpRequest` is a built-in browser object that allows to make HTTP requests in JavaScript. - -Despite of having the word "XML" in its name, it can operate on any data, not only in XML format. - -## Asynchronous XMLHttpRequest - -XMLHttpRequest has two modes of operation: synchronous and asynchronous. - -First let's see the asynchronous variant as it's used in the majority of cases. - -The code below loads the URL at `/article/xmlhttprequest/hello.txt` from the server and shows its content on-screen: - -```js run -*!* -// 1. Create a new XMLHttpRequest object -*/!* -let xhr = new XMLHttpRequest(); - -*!* -// 2. Configure it: GET-request for the URL /article/.../hello.txt -xhr.open('GET', '/article/xmlhttprequest/hello.txt'); -*/!* - -*!* -// 3. Send the request over the network -*/!* -xhr.send(); - -*!* -// 4. This will be called after the response is received -*/!* -xhr.onload = function() { - if (xhr.status != 200) { // analyze HTTP status of the response - // if it's not 200, consider it an error - alert(xhr.status + ': ' + xhr.statusText); // e.g. 404: Not Found - } else { - // show the result - alert(xhr.responseText); // responseText is the server response - } -}; -``` - -As we can see, there are several methods of `XMLHttpRequest` here. Let's cover them. - -## Setup: "open" - -The syntax: -```js -xhr.open(method, URL, async, user, password) -``` - -This method is usually called first after `new XMLHttpRequest`. It specifies the main parameters of the request: - -- `method` -- HTTP-method. Usually `"GET"` or `"POST"`, but we can also use TRACE/DELETE/PUT and so on. -- `URL` -- the URL to request. Can use any path and protocol, but there are cross-domain limitations called "Same Origin Policy". We can make any requests to the same `protocol://domain:port` that the current page comes from, but other locations are "forbidden" by default (unless they implement special HTTP-headers, we'll cover them in chapter [todo]). -- `async` -- if the third parameter is explicitly set to `false`, then the request is synchronous, otherwise it's asynchronous. We'll talk more about that in this chapter soon. -- `user`, `password` -- login and password for basic HTTP auth (if required). - -Please note that `open` call, contrary to its name, does not open the connection. It only configures the request, but the network activity only starts with the call of `send`. - -## Send it out: "send" - -The syntax: -```js -xhr.send([body]) -``` - -This method opens the connection and sends the request to server. The optional `body` parameter contains the request body. Some request methods like `GET` do not have a body. And some of them like `POST` use `body` to send the data. We'll see examples with a body in the next chapter. - - -## Cancel: abort and timeout - -If we changed our mind, we can terminate the request at any time. The call to `xhr.abort()` does that: - -```js -xhr.abort(); // terminate the request -``` - -We can also specify a timeout using the corresponding property: - -```js -xhr.timeout = 10000; -``` - -The timeout is expressed in ms. If the request does not succeed within the given time, it gets canceled automatically. - -## Events: onload, onerror etc - -A request is asynchronous by default. In other words, the browser sends it out and allows other JavaScript code to execute. - -After the request is sent, `xhr` starts to generate events. We can use `addEventListener` or `on<event>` properties to handle them, just like with DOM objects. - -The modern [specification](https://xhr.spec.whatwg.org/#events) lists following events: - -- `loadstart` -- the request has started. -- `progress` -- the browser received a data packet (can happen multiple times). -- `abort` -- the request was aborted by `xhr.abort()`. -- `error` -- an network error has occured, the request failed. -- `load` -- the request is successful, no errors. -- `timeout` -- the request was canceled due to timeout (if the timeout is set). -- `loadend` -- the request is done (with an error or without it) -- `readystatechange` -- the request state is changed (will cover later). - -Using these events we can track successful loading (`onload`), errors (`onerror`) and the amount of the data loaded (`onprogress`). - -Please note that errors here are "communication errors". In other words, if the connection is lost or the remote server does not respond at all -- then it's the error in the terms of XMLHttpRequest. Bad HTTP status like 500 or 404 are not considered errors. - -Here's a more feature-full example, with errors and a timeout: - -```html run -<script> - function load(url) { - let xhr = new XMLHttpRequest(); - xhr.open('GET', url); - xhr.timeout = 1000; - xhr.send(); - - xhr.onload = function() { - alert(`Loaded: ${this.status} ${this.responseText}`); - }; - - xhr.onerror = () => alert('Error'); - - xhr.ontimeout = () => alert('Timeout!'); - } -</script> - -<button onclick="load('/article/xmlhttprequest/hello.txt')">Load</button> -<button onclick="load('/article/xmlhttprequest/hello.txt?speed=0')">Load with timeout</button> -<button onclick="load('no-such-page')">Load 404</button> -<button onclick="load('http://example.com')">Load another domain</button> -``` - -1. The first button triggers only `onload` as it loads the file `hello.txt` normally. -2. The second button loads a very slow URL, so it calls only `ontimeout` (because `xhr.timeout` is set). -3. The third button loads a non-existant URL, but it also calls `onload` (with "Loaded: 404"), because there's no network error. -4. The last button tries to load a page from another domain. That's prohibited unless the remote server explicitly agrees by sending certain headers (to be covered later), so we have `onerror` here. The `onerror` handler would also trigger in other cases if we start a request, and then sever the network connection of our device. - -## Response: status, responseText and others - -Once the server has responded, we can receive the result in the following properties of the request object: - -`status` -: HTTP status code: `200`, `404`, `403` and so on. Also can be `0` if an error occured. - -`statusText` -: HTTP status message: usually `OK` for `200`, `Not Found` for `404`, `Forbidden` for `403` and so on. - -`responseText` -: The text of the server response, - -Есть и ещё одно свойство, которое используется гораздо реже: - -`responseXML` -: Если сервер вернул XML, снабдив его правильным заголовком `Content-type: text/xml`, то браузер создаст из него XML-документ. По нему можно будет делать запросы `xhr.responseXml.querySelector("...")` и другие. - - Оно используется редко, так как обычно используют не XML, а JSON. То есть, сервер возвращает JSON в виде текста, который браузер превращает в объект вызовом `JSON.parse(xhr.responseText)`. - -## Синхронные и асинхронные запросы - -Если в методе `open` установить параметр `async` равным `false`, то запрос будет синхронным. - -Синхронные вызовы используются чрезвычайно редко, так как блокируют взаимодействие со страницей до окончания загрузки. Посетитель не может даже прокручивать её. Никакой JavaScript не может быть выполнен, пока синхронный вызов не завершён -- в общем, в точности те же ограничения как `alert`. - -```js -// Синхронный запрос -xhr.open('GET', 'phones.json', *!*false*/!*); - -// Отсылаем его -xhr.send(); -*!* -// ...весь JavaScript "подвиснет", пока запрос не завершится -*/!* -``` - -Если синхронный вызов занял слишком много времени, то браузер предложит закрыть "зависшую" страницу. - -Из-за такой блокировки получается, что нельзя отослать два запроса одновременно. Кроме того, забегая вперёд, заметим, что ряд продвинутых возможностей, таких как возможность делать запросы на другой домен и указывать таймаут, в синхронном режиме не работают. - -Из всего вышесказанного уже должно быть понятно, что синхронные запросы используются чрезвычайно редко, а асинхронные -- почти всегда. - -Для того, чтобы запрос стал асинхронным, укажем параметр `async` равным `true`. - -Изменённый JS-код: - -```js -var xhr = new XMLHttpRequest(); - -xhr.open('GET', 'phones.json', *!*true*/!*); - -xhr.send(); // (1) - -*!* -xhr.onreadystatechange = function() { // (3) - if (xhr.readyState != 4) return; -*/!* - - button.innerHTML = 'Готово!'; - - if (xhr.status != 200) { - alert(xhr.status + ': ' + xhr.statusText); - } else { - alert(xhr.responseText); - } - -} - -button.innerHTML = 'Загружаю...'; // (2) -button.disabled = true; -``` - -Если в `open` указан третий аргумент `true` (или если третьего аргумента нет), то запрос выполняется асинхронно. Это означает, что после вызова `xhr.send()` в строке `(1)` код не "зависает", а преспокойно продолжает выполняться, выполняется строка `(2)`, а результат приходит через событие `(3)`, мы изучим его чуть позже. - -Полный пример в действии: - -[codetabs src="phones-async"] - -# Событие readystatechange - -Событие `readystatechange` происходит несколько раз в процессе отсылки и получения ответа. При этом можно посмотреть "текущее состояние запроса" в свойстве `xhr.readyState`. - -В примере выше мы использовали только состояние `4` (запрос завершён), но есть и другие. - -Все состояния, по [спецификации](http://www.w3.org/TR/XMLHttpRequest/#states): - -```js -const unsigned short UNSENT = 0; // начальное состояние -const unsigned short OPENED = 1; // вызван open -const unsigned short HEADERS_RECEIVED = 2; // получены заголовки -const unsigned short LOADING = 3; // загружается тело (получен очередной пакет данных) -const unsigned short DONE = 4; // запрос завершён -``` - -Запрос проходит их в порядке `0` -> `1` -> `2` -> `3` -> ... -> `3` -> `4`, состояние `3` повторяется при каждом получении очередного пакета данных по сети. - -Пример ниже демонстрирует переключение между состояниями. В нём сервер отвечает на запрос `digits`, пересылая по строке из 1000 цифр раз в секунду. - -[codetabs src="readystate"] - -```warn header="Точка разрыва пакетов не гарантирована" -При состоянии `readyState=3` (получен очередной пакет) мы можем посмотреть текущие данные в `responseText` и, казалось бы, могли бы работать с этими данными как с "ответом на текущий момент". - -Однако, технически мы не управляем разрывами между сетевыми пакетами. Если протестировать пример выше в локальной сети, то в большинстве браузеров разрывы будут каждые 1000 символов, но в реальности пакет может прерваться на любом байте. - -Чем это опасно? Хотя бы тем, что символы русского языка в кодировке UTF-8 кодируются двумя байтами каждый -- и разрыв может возникнуть *между ними*. - -Получится, что при очередном `readyState` в конце `responseText` будет байт-полсимвола, то есть он не будет корректной строкой -- частью ответа! Если в скрипте как-то по-особому это не обработать, то неизбежны проблемы. -``` - -## HTTP-заголовки - -`XMLHttpRequest` умеет как указывать свои заголовки в запросе, так и читать присланные в ответ. - -Для работы с HTTP-заголовками есть 3 метода: - -`setRequestHeader(name, value)` -: Устанавливает заголовок `name` запроса со значением `value`. - - Например: - - ```js - xhr.setRequestHeader('Content-Type', 'application/json'); - ``` - -```warn header="Ограничения на заголовки" -Нельзя установить заголовки, которые контролирует браузер, например `Referer` или `Host` и ряд других (полный список [тут](http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader-method)). - -Это ограничение существует в целях безопасности и для контроля корректности запроса. -``` - -````warn header="Поставленный заголовок нельзя снять" -Особенностью `XMLHttpRequest` является то, что отменить `setRequestHeader` невозможно. - -Повторные вызовы лишь добавляют информацию к заголовку, например: - -```js -xhr.setRequestHeader('X-Auth', '123'); -xhr.setRequestHeader('X-Auth', '456'); - -// в результате будет заголовок: -// X-Auth: 123, 456 -``` -```` - -`getResponseHeader(name)` -: Возвращает значение заголовка ответа `name`, кроме `Set-Cookie` и `Set-Cookie2`. - - Например: - - ```js - xhr.getResponseHeader('Content-Type') - ``` - -`getAllResponseHeaders()` -: Возвращает все заголовки ответа, кроме `Set-Cookie` и `Set-Cookie2`. - - Заголовки возвращаются в виде единой строки, например: - - ``` - Cache-Control: max-age=31536000 - Content-Length: 4260 - Content-Type: image/png - Date: Sat, 08 Sep 2012 16:53:16 GMT - ``` - - Между заголовками стоит перевод строки в два символа `"\r\n"` (не зависит от ОС), значение заголовка отделено двоеточием с пробелом `": "`. Этот формат задан стандартом. - - Таким образом, если хочется получить объект с парами заголовок-значение, то эту строку необходимо разбить и обработать. - -## Таймаут - -Максимальную продолжительность асинхронного запроса можно задать свойством `timeout`: - -```js -xhr.timeout = 30000; // 30 секунд (в миллисекундах) -``` - -При превышении этого времени запрос будет оборван и сгенерировано событие `ontimeout`: - -```js -xhr.ontimeout = function() { - alert( 'Извините, запрос превысил максимальное время' ); -} -``` - -## Полный список событий - -Современная [спецификация](http://www.w3.org/TR/XMLHttpRequest/#events) предусматривает следующие события по ходу обработки запроса: - -- `loadstart` -- запрос начат. -- `progress` -- браузер получил очередной пакет данных, можно прочитать текущие полученные данные в `responseText`. -- `abort` -- запрос был отменён вызовом `xhr.abort()`. -- `error` -- произошла ошибка. -- `load` -- запрос был успешно (без ошибок) завершён. -- `timeout` -- запрос был прекращён по таймауту. -- `loadend` -- запрос был завершён (успешно или неуспешно) - -Используя эти события можно более удобно отслеживать загрузку (`onload`) и ошибку (`onerror`), а также количество загруженных данных (`onprogress`). - -Ранее мы видели ещё одно событие -- `readystatechange`. Оно появилось гораздо раньше, ещё до появления текущего стандарта. - -В современных браузерах от него можно отказаться в пользу других, необходимо лишь, как мы увидим далее, учесть особенности IE8-9. - -## IE8,9: XDomainRequest - -В IE8 и IE9 поддержка `XMLHttpRequest` ограничена: - -- Не поддерживаются события, кроме `onreadystatechange`. -- Некорректно поддерживается состояние `readyState = 3`: браузер может сгенерировать его только один раз во время запроса, а не при каждом пакете данных. Кроме того, он не даёт доступ к ответу `responseText` до того, как он будет до конца получен. - -Дело в том, что, когда создавались эти браузеры, спецификации были не до конца проработаны. Поэтому разработчики браузера решили добавить свой объект `XDomainRequest`, который реализовывал часть возможностей современного стандарта. - -А обычный `XMLHttpRequest` решили не трогать, чтобы ненароком не сломать существующий код. - -Мы подробнее поговорим про `XDomainRequest` в главе <info:xhr-crossdomain>. Пока лишь заметим, что для того, чтобы получить некоторые из современных возможностей в IE8,9 -- вместо `new XMLHttpRequest()` нужно использовать `new XDomainRequest`. - -Кросс-браузерно: - -```js -var XHR = ("onload" in new XMLHttpRequest()) ? XMLHttpRequest : XDomainRequest; -var xhr = new XHR(); -``` - -Теперь в IE8,9 поддерживаются события `onload`, `onerror` и `onprogress`. Это именно для IE8,9. Для IE10 обычный `XMLHttpRequest` уже является полноценным. - -### IE9- и кеширование - -Обычно ответы на запросы `XMLHttpRequest` кешируются, как и обычные страницы. - -Но IE9- по умолчанию кеширует все ответы, не снабжённые антикеш-заголовком. Другие браузеры этого не делают. Чтобы этого избежать, сервер должен добавить в ответ соответствующие антикеш-заголовки, например `Cache-Control: no-cache`. - -Впрочем, использовать заголовки типа `Expires`, `Last-Modified` и `Cache-Control` рекомендуется в любом случае, чтобы дать понять браузеру (не обязательно IE), что ему следует делать. - -Альтернативный вариант -- добавить в URL запроса случайный параметр, предотвращающий кеширование. - -Например, вместо `xhr.open('GET', 'service', false)` написать: - -```js -xhr.open('GET', *!*'service?r=' + Math.random()*/!*, false); -``` - -По историческим причинам такой способ предотвращения кеширования можно увидеть много где, так как старые браузеры плохо обрабатывали кеширующие заголовки. Сейчас серверные заголовки поддерживаются хорошо. - -## Итого - -Типовой код для GET-запроса при помощи `XMLHttpRequest`: - -```js -var xhr = new XMLHttpRequest(); - -xhr.open('GET', '/my/url', true); - -xhr.send(); - -xhr.onreadystatechange = function() { - if (this.readyState != 4) return; - - // по окончании запроса доступны: - // status, statusText - // responseText, responseXML (при content-type: text/xml) - - if (this.status != 200) { - // обработать ошибку - alert( 'ошибка: ' + (this.status ? this.statusText : 'запрос не удался') ); - return; - } - - // получить результат из this.responseText или this.responseXML -} -``` - -Мы разобрали следующие методы `XMLHttpRequest`: - -- `open(method, url, async, user, password)` -- `send(body)` -- `abort()` -- `setRequestHeader(name, value)` -- `getResponseHeader(name)` -- `getAllResponseHeaders()` - -Свойства `XMLHttpRequest`: - -- `timeout` -- `responseText` -- `responseXML` -- `status` -- `statusText` - -События: - -- `onreadystatechange` -- `ontimeout` -- `onerror` -- `onload` -- `onprogress` -- `onabort` -- `onloadstart` -- `onloadend` diff --git a/7-network/index.md b/7-network/index.md deleted file mode 100644 index 0acef10294..0000000000 --- a/7-network/index.md +++ /dev/null @@ -1,5 +0,0 @@ -development: true - ---- - -# ネットワークリクエスト: AJAX と COMET diff --git a/8-web-components/1-webcomponents-intro/article.md b/8-web-components/1-webcomponents-intro/article.md new file mode 100644 index 0000000000..7791450eb8 --- /dev/null +++ b/8-web-components/1-webcomponents-intro/article.md @@ -0,0 +1,76 @@ +# 導入(From the orbital height) + +このセクションでは、"web コンポーネント" に関する一連の最新の標準について説明します。 + +現時点では、これらの標準は開発中です。一部の機能は十分にサポートされており、最新の HTML/DOM の標準に統合されている一方、まだドラフト段階にあるものもあります。どのブラウザでも例を試すことはできますが、Google Chrome がおそらく最も最新です。Google のメンバが関連する仕様の多くを支持しているからだと思います。 + +## 共通点は... + +コンポーネントのアイデア自体は新しいものではありません。これは多くのフレームワークなどで使われています。 + +実装の詳細に移る前に、この素晴らしい人類の成果を見てください: + +![](satellite.jpg) + +これは国際宇宙ステーション(ISS)です。 + +そして、これは内部がどのように作られているかです(おおよそ) + +![](satellite-expanded.jpg) + +国際宇宙ステーション: +- 多数のコンポーネントから構成されています +- 各コンポーネントは、今度は自身が内部に多くの小さな細部を持っています。 +- コンポーネントは非常に複雑で、ほとんどの webサイトよりもはるかに複雑です。 +- コンポーネントは、さまざまな言語を話す異なる国のチームによって国際的に開発されています。 + +...そして、これは飛び、人類は宇宙で生きることができます! + +どのようにして、これほど複雑なデバイスは作られるのでしょうか? + +我々の開発に同レベルの信頼性と拡張性を実現する、あるいは少なくともそれに近づくためには、どの原則を取り入れることができますか? + +## コンポーネントアーキテクチャ + +複雑なソフトウェアを開発するための広く知られている規則は、複雑なソフトウェアを作らない、です。 + +何かが複雑になった場合、それをより単純な複数のパーツに分割し、最も明白な方法でそれらをつなぎます。 + +**良いアーキテクトは、複雑なものを単純にできる人です。** + +ユーザーインターフェースは視覚的なコンポーネントに分割することができます。それぞれがページ上に自身の場所を持ち、あるモデルによってきちんと説明されるタスクを "実行" でき、他のものとは別ものです。 + +例で Twitter などの webサイトを見てみましょう。 + +自然にコンポーネントに分割されます。 + +![](web-components-twitter.svg) + +1. トップナビゲーション +2. ユーザ情報 +3. おすすめユーザ +4. 投稿フォーム. +5. (6, 7も) -- メッセージ. + +コンポーネントはサブコンポーネントを持つことがあります。e.g. メッセージは上位の "メッセージ一覧" コンポーネントの一部かもしれません。クリック可能なユーザ画像自体がコンポーネントになったりすることもあります。 + +何がコンポーネントかはどのようにして決めますか?それは直感や経験、一般常識からきます。通常それは、それが何をするのか、またページとどのように相互作用するのかという観点から説明できる視覚的なエンティティです。上の例では、ページには区切りがあり、それぞれが自身の役割を果たしています。そのため、これらのコンポーネントを作るのは理にかなっています。 + +コンポーネントは次のものを持っています。: +- 独自の JavaScript クラス +- DOM 構造: これはそのクラスによってのみ管理され、外部のコードはそこにアクセスしません("カプセル化"の原則)。 +- CSS スタイル: コンポーネントに適用されます。 +- API: 他のコンポーネントとやり取りするためのイベントやクラスメソッドなどです。 + +繰り返しますが、"コンポーネント" というもの自体は特別なものではありません。 + +それらを構築するための多くのフレームワークと開発方法論があり、それぞれ独自のオプション機能を持っています。通常は、"コンポーネント"(CSS スコープと DOM のカプセル化) を提供するのに、特別なCSSクラスと規約が使用されます。 + +"Web コンポーネント" はそのためのブラウザ組み込みの機能です。なので、もうそれらを自分たちでエミュレートする必要はありません。 + +- [Custom elements](https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements) -- カスタムのHTML要素を定義します。 +- [Shadow DOM](https://dom.spec.whatwg.org/#shadow-trees) -- 他からは隠された、コンポーネント用の内部DOMを作成します。 +- [CSS Scoping](https://drafts.csswg.org/css-scoping/) -- コンポーネントの Shadow DOM 内にのみ適用されるスタイルを宣言します。 +- [Event retargeting](https://dom.spec.whatwg.org/#retarget) やその他のいくつかの機能は、カスタムコンポーネントを開発により適したものにするためのものです。 + +次のチャプターでは、"Custom Elements" -- Web コンポーネントの基本的かつよくサポートされている機能の詳細を見ていきます。 \ No newline at end of file diff --git a/8-web-components/1-webcomponents-intro/satellite-expanded.jpg b/8-web-components/1-webcomponents-intro/satellite-expanded.jpg new file mode 100644 index 0000000000..56c1271121 Binary files /dev/null and b/8-web-components/1-webcomponents-intro/satellite-expanded.jpg differ diff --git a/8-web-components/1-webcomponents-intro/satellite-expanded@2x.jpg b/8-web-components/1-webcomponents-intro/satellite-expanded@2x.jpg new file mode 100644 index 0000000000..c65b124502 Binary files /dev/null and b/8-web-components/1-webcomponents-intro/satellite-expanded@2x.jpg differ diff --git a/8-web-components/1-webcomponents-intro/satellite.jpg b/8-web-components/1-webcomponents-intro/satellite.jpg new file mode 100644 index 0000000000..9a3793f357 Binary files /dev/null and b/8-web-components/1-webcomponents-intro/satellite.jpg differ diff --git a/8-web-components/1-webcomponents-intro/satellite@2x.jpg b/8-web-components/1-webcomponents-intro/satellite@2x.jpg new file mode 100644 index 0000000000..9624379d5b Binary files /dev/null and b/8-web-components/1-webcomponents-intro/satellite@2x.jpg differ diff --git a/8-web-components/1-webcomponents-intro/web-components-twitter.svg b/8-web-components/1-webcomponents-intro/web-components-twitter.svg new file mode 100644 index 0000000000..9d3b0b00b3 --- /dev/null +++ b/8-web-components/1-webcomponents-intro/web-components-twitter.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="716" height="399" viewBox="0 0 716 399"><defs><style>@import url(https://fonts.googleapis.com/css?family=Open+Sans:bold,italic,bolditalic%7CPT+Mono);@font-face{font-family:'PT Mono';font-weight:700;font-style:normal;src:local('PT MonoBold'),url(/font/PTMonoBold.woff2) format('woff2'),url(/font/PTMonoBold.woff) format('woff'),url(/font/PTMonoBold.ttf) format('truetype')}</style></defs><g id="dom" fill="none" fill-rule="evenodd" stroke="none" stroke-width="1"><g id="web-components-twitter.svg"><image id="Снимок-экрана-2019-03-28-в-20.29.32" width="717" height="407" x="0" y="0" xlink:href="data:image/png;base64,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6rp2Tdh1dXfo7qZ6l/rNe5h7yvaCvYq9f/6a/uutfVH7Wvb77K8/YHug+iDjYGkD0jC9oadR1NjelNrUdmj0oZZm/+aDh4cf3n7E8kjVUcOjy45RjxUf6z9edLz3hPRE98mskx0tk1vunRp36sbpsadbz0SdOX82/Oypc+xzx88HnD9ywe/CoYs+FxsveV1quOx5+eBvnr8dbPVqbbjifaXpqu/V5rZRbceuBV07eT30+tkb3BuXbsbcbLuVdOvO7Qm32+8I7jy7m3v35e8Fv/fdm3ufcL/0ge6D8oemD2v+5fivPe1e7UcfhT66/Djh8b0OfsfzP+R/fOosfkJ/Uv7U4mntM/dnR7rCu67+Of7PzufS533dJX/p/VX9wuHFgb+D/77cM66n86XsZf+rJa+NX29/M/JNS29c78O3eW/73pW+N36/44PPh3MfUz4+7Zv2ifSp4rPj5+YvUV/u9+f190t5Mp5qK4DBgWZmAvBqOwD0VAAYV+H+Ybz6nKcSRH02VSHwn7D6LKgSLwDq4U25XeecAGAvHHbBkBvelVv1xGCAengMDo3IMz3c1Vw0eOIhvO/vf20GAAnuZz7L+vv71vf3f94Kk70LwImp6vOlUojwbLApWIluGk1cDL6TfwOWyYCwhO6N0AAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAFmqADAAQAAAABAAADLgAAAADjQlLXAABAAElEQVR4AeydB2Bkd3ntv+lVGvW6q+327uK1vca9YQPGBmMb7AA2AfySQAghLyGkkeSlkbwUEkIKJMAjARuIcQNjm2JjGxt3cN319qZdrVarrtH0qne+v3TlWVltV6PRSDofzE6793/v/d258sy5557PNoISFgmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmcIgH7Kc7H2UiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEjAEKDQzA8CCZAACZAACZAACZAACZAACZAACZAACZAACZAACZDAnAhQaJ4TPs5MAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAoZmfARIgARIgARIgARIgARIgARIgARIgARIgARIgARIggTkRoNA8J3ycmQRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgEIz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BEiABEiABEiABEiABEiABEiABEhguRBY+mEuy2VPcjtJgARIgARIgARIgARIgARIgARIgARIgARIgARIYIEIUGheIPBcLAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAksFQIUmpfKnuR2kAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkMACEaDQvEDguVgSIAESIAESIAESIAESIAESIAESIAESIAESIAESWCoEKDQvlT3J7SABEiABEiABEiABEiABEiABEiABEiABEiABEiCBBSLgnGq5v/JE71Rv8fWTIPD1t9SfxNSclARIgARIgARIgARIgARIgARIgARIgARIgARIgAQWHwE6mhffPuMakwAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkEBZEZjS0VxWa7mIV4bO8EW885bQqtNZv4R2JjeFBEiABEiABEiABEiABEiABEiABEiABMqQAB3NZbhTuEokQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIksJgI2B772dMji2mFua4kQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQALlRcB57Fhnea0R14YESIAESIAESIAESIAESIAESIAESIAESIAESIAESGBREbB19Q3S0byodhlXlgRIgARIgARIgARIgARIgARIgARIgARIgARIgATKiwAzmstrf3BtSIAESIAESIAESIAESIAESIAESIAESIAESIAESGDREaDQvOh2GVeYBEiABEiABEiABEiABEiABEiABEiABEiABEiABMqLAIXm8tofXBsSIAESIAESIAESIAESIAESIAESIAESIAESIAESWHQEKDQvul3GFSYBEiABEiABEiABEiABEiABEiABEiABEiABEiCB8iJAobm89gfXhgRIgARIgARIgARIgARIgARIgARIgARIgARIgAQWHQEKzYtul3GFSYAESIAESIAESIAESIAESIAESIAESIAESIAESKC8CFBoLq/9wbUhARIgARIgARIgARIgARIgARIgARIgARIgARIggUVHgELzottlXGESIAESIAESIAESIAESIAESIAESIAESIAESIAESKC8CFJrLa39wbUiABEiABEiABEiABEiABEiABEiABEiABEiABEhg0RGg0LzodhlXmARIgARIgARIgARIgARIgARIgARIgARIgARIgATKiwCF5vLaH1wbEiABEiABEiABEiABEiABEiABEiABEiABEiABElh0BCg0L7pdxhUmARIgARIgARIgARIgARIgARIgARIgARIgARIggfIiQKG5vPYH14YESIAESIAESIAESIAESIAESIAESIAESIAESIAEFh0B51Rr/MevZaZ6i6+TAAmQAAmQAAmMEfi7M1xkQQIkQAIkQAIkQAIkQAIkQAIkQALLngAdzcv+I0AAJEACJEACJEACJEACJEACJEACJEACJEACJEACJDA3AlM6muc2LOcmARIgARIgARI4FQKuYMWpzMZ5SIAESIAESIAESIAESIAESIAESGBeCWSikWnHp6N5Wjx8kwRIgARIgARIgARIgARIgARIgARIgARIgARIgARIYCYCFJpnIsT3SYAESIAESIAESIAESIAESIAESIAESIAESIAESIAEpiVAoXlaPHyTBEiABEiABEiABEiABEiABEiABEiABEiABEiABEhgJgIUmmcixPdJgARIgARIgARIgARIgARIgARIgARIgARIgARIgASmJUCheVo8fJMESIAESIAESIAESIAESIAESIAESIAESIAESIAESGAmAhSaZyLE90mABEiABEiABEiABEiABEiABEiABEiABEiABEiABKYlQKF5Wjx8kwRIgARIgARIgARIgARIgARIgARIgARIgARIgARIYCYCFJpnIsT3SYAESIAESIAESIAESIAESIAESIAESIAESIAESIAEpiVAoXlaPHyTBEiABEiABEiABEiABEiABEiABEiABEiABEiABEhgJgIUmmcixPdJgARIgARIgARIgARIgARIgARIgARIgARIgARIgASmJUCheVo8fJMESIAESIAESIAESIAESIAESIAESIAESIAESIAESGAmAhSaZyLE90mABEiABEiABEiABEiABEiABEiABEiABEiABEiABKYlQKF5Wjx8kwRIgARIgARIgARIgARIgARIgARIgARIgARIgARIYCYCFJpnIsT3SYAESIAESIAESIAESIAESIAESIAESIAESIAESIAEpiVAoXlaPHyTBEiABEiABEiABEiABEiABEiABEiABEiABEiABEhgJgLOmSbg+yRAAiRAAiRAAiRAAqdGIJUbkSRubrtNfE7cHHbxOGyC/7NIgARIgARIgARIgARIgARI4KQI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Gqta47a+Pc9lElQgmyCMV9cGPqdgFwStQxIBERABERABERABERCB3ZxAB44/inuOBtx78P6DA/8xaJfBTOYdITJz/xKaSUEhAiIgAiIgAiIgAtuAgM9sbsPras3IbGaHb117wk3boHpVIQIiIAIiIAIiIAIiIAIiIALdCISQ0cKB/wbakzm7IRKas4nouwiIgAiIgAiIgAhsJQF28IqCAYsgy4DZzUgywGtsSetMNNjK2rW5CIiACIiACIiACIiACIjA7kyACS6BvDyjDTOzmAswDYaQ0DwYzoLaIAIiIAIiIAIisEsSYIdvsHT6dknAOigREAEREAEREAEREAEREIFBQ0BWgYPmVKghIiACIiACIiACIiACIiACIiACIiACIiACIiACIrBzEpDQvHOeN7VaBERABERABERABERABERABERABERABERABERABAYNAQnNg+ZUqCEiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIisHMSkNC8c543tVoEREAEREAEREAEREAEREAEREAEREAEREAEREAEBg0BCc2D5lSoISIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiKwcxKQ0Lxznje1WgREQAREQAREQAREQAREQAREQAREQAREQAREQAQGDQEJzYPmVKghIiACIiACIiACIiACIiACIiACIiACIiACIiACIrBzEpDQvHOeN7VaBERABERABERABERABERABERABERABERABERABAYNAQnNg+ZUqCEiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIisHMSkNC8c543tVoEREAEREAEREAEREAEREAEREAEREAEREAEREAEBg0BCc2D5lSoISIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiKwcxKQ0Lxznje1WgREQAREQAREQAREQAREQAREQAREQAREQAREQAQGDQEJzYPmVKghIiACIiACIiACIiACIiACIiACIiACIiACIiACIrBzEpDQvHOeN7VaBERABERABERABERABERABERABERABERABERABAYNAQnNg+ZUqCEiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIisHMSCO6czVarRUAEREAERGDXJBBrbto1D0xHJQIiIAIiIAIiIAIiIAIiIAIisEsTUEbzLn16dXAiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIisP0JKKN5+zPWHkRABERABESg3wRGDKnsd1kVFAEREAEREAEREAEREAEREAEREIGBIrBmY32vu1JGc694tFIEREAEREAEREAEREAEREAEREAEREAEREAEREAERKAvAhKa+yKk9SIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAiIgAr0SkNDcKx6tFAEREAEREAEREAEREAEREAEREAEREAEREAEREAER6IuAhOa+CGm9CIiACIiACIiACIiACIiACIiACIiACIiACIiACIhArwQkNPeKRytFQAREQAREQAREQAREQAREQAREQAREQAREQAREQAT6IiChuS9CWi8CIiACIiACIiACIiACIiACIiACIiACIiACIiACItArAQnNveLRShEQAREQAREQAREQAREQAREQAREQAREQAREQAREQgb4IBPsqsLusH1UctGmVIZtYFrSRxQGrCOdbcTDfwvl5Fsgzy8PUWySTZglM0Y6ktcY7rCHaYbWtCVvSFLf59TFb3RrvbXOtEwEREAEREAEREAEREAEREAEREAEREAEREAEREIGdlsBuKTQXQDmeDlH50GGFNqk8aFUFAYOevFVBITrICRUVBwM2tDCAukN25IhUtdCfbVMkYYsb4/bCunZ7C+JzhMq0QgREQAREQAREQAREQAREQAREQAREQAREQAREQAR2cgK7jdBM4feIEQX2npoCZCwP/GFTyB6CNnA6ZFiB+9nUIsv5xfURe3ZNxDa0J3byn5KaLwIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIisLsSGHjFdQBJh6DuHgVx+f2ji50dxgDuul+7ouD9kT04lTibjcdWtdp/ITrHmP6sEAEREAEREAEREAEREAEREAEREAEREAEREAEREIGdhMAuKTSPLgnaSeOK7SBkL9NfeUuipbnZmjG1tbZae1ubtbe0WnNLsyUh/iZhxNzR0WGtLS2WSCQsGAxaUVGRBUMheDjnWWlZqYULCi0cDlsJ5kvLyt2y/uyfvtAfm1Jm50wus1eQ5Xz/8lZb1SJf5/6wUxkREAEREAEREAEREAEREAEREAEREAEREAEREIEdS2CXEponwxP59IklNq0i1C+qFIvrNm6w+vp6a2xowPxGa29vNyjKEIwhUls+BOYOi0ajTkjOz89P10uRmYIy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QFrPv1keozmkZxuiwhDuAbFUGxQLUYEsRhOA6BaTT8WoTUccZTivl1HiMv5oJOAvgS/XJWiRGfkrKA/7jDQMmvg7cOJUj2DuD6fymEYCJEACJEACJEACJEACJEACJEACJEACmUkgY5WXzDwTHosESIAELASeWhAn1ljMt9QobNnKRRIgARIgARIgARIgARIgARIgARIgARIggVASoNAcSposiwRIINsQOHbWFRUIUW/614rSoVqyTeVYERIgARIgARIgARIgARIgARIgARIgARLIZQQoNOeyC8rTIQEScBH4+tKMi8dMxiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAp4E/J+pynM/rpEACZAACZAACZAACZAACZAACZAACZAACZAACZAACZCAJkChmQ2BBEiABEiABEiABEiABEiABEiABEiABEiABEiABEggKAIUmoPCx51JgARIgARIgARIgARIgARIgARIgARIgARIgARIgAQoNLMNkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJBEWAQnNQ+LgzCZAACZAACZAACZAACZAACZAACZAACZAACZAACZAAhWa2ARIgARIgARIgARIgARIgARIgARIgARIgARIgARIggaAIUGgOCh93JgESIAESIAESIAESIAESIAESIAESIAESIAESIAESoNDMNkACJEACJEACJEACJEACJEACJEACJEACJEACJEACJBAUgYig9ubOJEACJEACJEACJEACQRE4dy5J9uz/T+KPHJU8+JcHf6I/CxcqKBXKlpa8efmTLSjI3JkESIAESIAESIAESIAESCDDCfCpJcMR8wAkQAIkQAIkQAIk4Eng5KnTMnbyLNm0bafsO3BQkpLOe2awrIWF5ZHSJUtIbMWy0qNjWylZPMaylYskQAI5icCFCxfk9JmzkpiYKOfPXxDc35GRkZIvMq8eXLKeC/KeOZsoZ8+edefNmzev5M8XmSqvdT8uZz8Cp06fkW0798ixhBNBVw5tpXKFslIspmjQZbEAEiABEiABEgg1AQrNoSbK8kiABEiABEiABEjAB4FV6zbJ0JHj5WjCcYmICNcey5XKl9GigdKVBOKS+k/Oq7/jJ07Krj37Zfe+/7QgvWTFOrnm8o7SvvUl2uvZx2G4KZsRSDx3TvbuP6gFIqeqnVXC4/pN29WgQ5LTZiVG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7BMPN5IACZAACZBA5hKg0Jy5vLPL0YzQbOrTuF4tFYe1h5oAjrGYDZPc9InYytt27fUZr7lerarKe7WK42kvW7VeChTI53W7405MzJYEcO+PmzJLh8iprMKrtFBhVWALl6+W7aqNxFYoJz07t5M6NZzbQrY8KVbKg8C+AwdlyLBRsnf/fx7pwawUyJ9Pf0c0b1wvmGK4LwmQAAmQAAkETIBCc8DIuAMJkAAJkAAJZB0BCs1Zxz4rj7xl+y55/8uf9SR/N17VTZo1rJOV1eGxSYAEMpHAAeXtOnfRCjXh30o5fvKUPnJhNdlfq6YNpU3zRlKqRLFMrA0PlVEEDsUfUYNLx4MuPjIyUsqWKiHh4QxlFDRMFkACJEACJBAwAQrNASPjDiRAAiRAAiSQdQQoNGcd+6w+8satO6R8mVJSSAlMNBIggYuLQKKa1PHQ4SN6YlBEx6hTo6oUjy6qJwC8uEjwbEmABEiABEiABLIzAQrN2fnqsG4kQAIkQAIkYCNAodkGhKskQAIkQAIkQAIkQAIkQAIkQALZgkBaQjPft8kWl4mVIAESIAESIAESIAESIAESIAESIAESIAESIAESIIGcS4BCc869dqw5CZAACZAACZAACZAACZAACZAACZAACZAACZAACWQLAhSas8VlYCVIgARIgARIgARIgARIgARIgARIgARIgARIgARIIOcSoNCcc68da04CJEACJEACJEACJEACJEACJEACJEACJEACJEAC2YIAheZscRlYCRIgARIgARIgARIgARIgARIgARIgARIgARIgARLIuQQoNOfca8eakwAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkEC2IEChOVtcBlaCBEiABEiABEiABEiABEiABEiABEiABEiABEiABHIuAQrNOffaseYkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkkC0IUGjOFpeBlSABEiABEiABEiABEiABEiABEiABEiABEiABEiCBnEsgwlvVyxSP9raJ6SRAAiRAAiRAAskE9scdIQsSIAESIAESIAESIAESIAESIAESuOgJ0KP5om8CBEACJEACJEACJEACJEACJEACJEACJEACJEACJEACwRGg0BwcP+5NAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAhc9AQrNF30TIAASIAESIAESIAES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QCDZjTZdlIxoThcxi28IGAKGgCFgCBgChoAhYAgYAoaAIVARCCSb/1aviEpUxjJ0ObuqrqrLEbvLObt3TVhNjZswggUYAoaAIWAIGAKGgCFgCBgChoAhYAgYAoaAIWAIGAKGgCGwCSNQdRM+dzt1Q8AQMAQMAUPAEDAEDAFDwBAwBAwBQ+D/2TsPwCqq9O2/UhMghZCQAoEAoffei0oRLCCoiB172YK7rtvU/bu73xbdteuKil1AehGpIkqV3ltCAgFCQnovFL/znMu5TG7aTXKT3ITn1cnMnDlz5pzfnZB7n3nvc0iABEiABEiABEjABQQoNLsAIpsgARIgARIgARIgARIgARIgARIgARIgARIgARIggWuZAIXma/nV59hJgARIgARIgARIgARIgARIgARIgARIgARIgARIwAUEKDS7ACKbIAESIAESIAESIAESIAESIAESIAESIAESIAESIIFrmQCF5mv51efYSYAESIAESIAESIAESIAESIAESIAESIAESIAESMAFBCg0uwAimyABEiABEiABEiABEiABEiABEiABEiABEiABEiCBa5kAheZr+dXn2EmABEiABEiABEiABEiABEiABEiABEiABEiABEjABQQoNLsAIpsgARIgARIgARIgARIgARIgARIgARIgARIgARIggWuZAIXma/nV59hJgARIgARIgARIgARIgARIgARIgARIgARIgARIwAUEKDS7ACKbIAESIAESIAESIAESIAESIAESIAESIAESIAESIIFrmQCF5mv51efYSYAESIAESIAESIAESIAESIAESIAESIAESIAESMAFBOq5oI1yNRGXlFqu83gSCZAACZAACZSFQFAz37JUZ10SIAESIAESIAESIAESIAESIAESIIFyEGBGczmg8RQSIAESIAESIAESIAESIAESIAESIAESIAESIAESIIGrBKoto5kZZldfBG6RAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQQE0mUG1Cc02Gxr6TAAmQAAmQAAmQgKsIXLx4Sc7GnZfk1DS5Dv9dh0X0uknjRtIyOFDq1+dbNlfxZjskQAIkQAIkQAIkQAIkQAKVQ4CfWiqHK1slARIgARIgARIggWIJZOfkyop1GyUiOkbOxSfIpUuXi61bp851EhjgL2GhwXLT9UMloFnTYuvyAAmQgHsT+Pnn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"/><path id="Rectangle" stroke="#C06334" stroke-width="3" d="M1.5 1.5h714v33H1.5z"/><path id="Rectangle-Copy" stroke="#C06334" stroke-width="3" d="M1.5 43.5h230v169H1.5z"/><path id="Rectangle-Copy-2" stroke="#C06334" stroke-width="3" d="M241.5 95.5h473v128h-473z"/><path id="Rectangle-Copy-3" stroke="#C06334" stroke-width="3" d="M241.5 333.5h473v64h-473z"/><path id="Rectangle-Copy-6" stroke="#C06334" stroke-width="3" d="M241.5 228.5h473v101h-473z"/><path id="Rectangle-Copy-4" stroke="#C06334" stroke-width="3" d="M714.5 47.5v43h-473v-43h473z"/><path id="Rectangle-Copy-5" stroke="#C06334" stroke-width="3" d="M1.5 221.5h230v176H1.5z"/><circle id="Oval-5-Copy" cx="18" cy="17" r="12" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2"/><text id="1" fill="#181717" font-family="PTMono-Bold, PT Mono" font-size="18" font-weight="bold"><tspan x="13" y="22">1</tspan></text><circle id="Oval-5-Copy" cx="18" cy="60" r="12" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2"/><text id="2" fill="#181717" font-family="PTMono-Bold, PT Mono" font-size="18" font-weight="bold"><tspan x="13" y="65">2</tspan></text><circle id="Oval-5-Copy-2" cx="259" cy="65" r="12" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2"/><text id="4" fill="#181717" font-family="PTMono-Bold, PT Mono" font-size="18" font-weight="bold"><tspan x="254" y="70">4</tspan></text><circle id="Oval-5-Copy-3" cx="19" cy="238" r="12" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2"/><text id="3-copy" fill="#181717" font-family="PTMono-Bold, PT Mono" font-size="18" font-weight="bold"><tspan x="14" y="243">3</tspan></text><circle id="Oval-5-Copy" cx="259" cy="113" r="12" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2"/><text id="5" fill="#181717" font-family="PTMono-Bold, PT Mono" font-size="18" font-weight="bold"><tspan x="254" y="118">5</tspan></text><circle id="Oval-5-Copy-4" cx="259" cy="246" r="12" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2"/><text id="6" fill="#181717" font-family="PTMono-Bold, PT Mono" font-size="18" font-weight="bold"><tspan x="254" y="251">6</tspan></text><circle id="Oval-5-Copy-5" cx="259" cy="351" r="12" fill="#FBF2EC" stroke="#DBAF88" stroke-width="2"/><text id="7" fill="#181717" font-family="PTMono-Bold, PT Mono" font-size="18" font-weight="bold"><tspan x="254" y="356">7</tspan></text></g></g></svg> \ No newline at end of file diff --git a/8-web-components/2-custom-elements/1-live-timer/solution.md b/8-web-components/2-custom-elements/1-live-timer/solution.md new file mode 100644 index 0000000000..a9eacc8803 --- /dev/null +++ b/8-web-components/2-custom-elements/1-live-timer/solution.md @@ -0,0 +1,4 @@ + +Please note: +1. We clear `setInterval` timer when the element is removed from the document. That's important, otherwise it continues ticking even if not needed any more. And the browser can't clear the memory from this element and referenced by it. +2. We can access current date as `elem.date` property. All class methods and properties are naturally element methods and properties. diff --git a/8-web-components/2-custom-elements/1-live-timer/solution.view/index.html b/8-web-components/2-custom-elements/1-live-timer/solution.view/index.html new file mode 100644 index 0000000000..e1dd85096e --- /dev/null +++ b/8-web-components/2-custom-elements/1-live-timer/solution.view/index.html @@ -0,0 +1,9 @@ +<!doctype html> +<script src="time-formatted.js"></script> +<script src="live-timer.js"></script> + +<live-timer id="elem"></live-timer> + +<script> + elem.addEventListener('tick', event => console.log(event.detail)); +</script> diff --git a/8-web-components/2-custom-elements/1-live-timer/solution.view/live-timer.js b/8-web-components/2-custom-elements/1-live-timer/solution.view/live-timer.js new file mode 100644 index 0000000000..a53d72e001 --- /dev/null +++ b/8-web-components/2-custom-elements/1-live-timer/solution.view/live-timer.js @@ -0,0 +1,32 @@ +class LiveTimer extends HTMLElement { + + render() { + this.innerHTML = ` + <time-formatted hour="numeric" minute="numeric" second="numeric"> + </time-formatted> + `; + + this.timerElem = this.firstElementChild; + } + + connectedCallback() { // (2) + if (!this.rendered) { + this.render(); + this.rendered = true; + } + this.timer = setInterval(() => this.update(), 1000); + } + + update() { + this.date = new Date(); + this.timerElem.setAttribute('datetime', this.date); + this.dispatchEvent(new CustomEvent('tick', { detail: this.date })); + } + + disconnectedCallback() { + clearInterval(this.timer); // important to let the element be garbage-collected + } + +} + +customElements.define("live-timer", LiveTimer); diff --git a/8-web-components/2-custom-elements/1-live-timer/solution.view/time-formatted.js b/8-web-components/2-custom-elements/1-live-timer/solution.view/time-formatted.js new file mode 100644 index 0000000000..4fab99cb25 --- /dev/null +++ b/8-web-components/2-custom-elements/1-live-timer/solution.view/time-formatted.js @@ -0,0 +1,34 @@ +class TimeFormatted extends HTMLElement { + + render() { + let date = new Date(this.getAttribute('datetime') || Date.now()); + + this.innerHTML = new Intl.DateTimeFormat("default", { + year: this.getAttribute('year') || undefined, + month: this.getAttribute('month') || undefined, + day: this.getAttribute('day') || undefined, + hour: this.getAttribute('hour') || undefined, + minute: this.getAttribute('minute') || undefined, + second: this.getAttribute('second') || undefined, + timeZoneName: this.getAttribute('time-zone-name') || undefined, + }).format(date); + } + + connectedCallback() { + if (!this.rendered) { + this.render(); + this.rendered = true; + } + } + + static get observedAttributes() { + return ['datetime', 'year', 'month', 'day', 'hour', 'minute', 'second', 'time-zone-name']; + } + + attributeChangedCallback(name, oldValue, newValue) { + this.render(); + } + +} + +customElements.define("time-formatted", TimeFormatted); diff --git a/8-web-components/2-custom-elements/1-live-timer/source.view/index.html b/8-web-components/2-custom-elements/1-live-timer/source.view/index.html new file mode 100644 index 0000000000..878120241d --- /dev/null +++ b/8-web-components/2-custom-elements/1-live-timer/source.view/index.html @@ -0,0 +1,12 @@ +<!doctype html> +<!-- don't modify this --> +<script src="time-formatted.js"></script> + +<!-- your code here: --> +<script src="live-timer.js"></script> + +<live-timer id="elem"></live-timer> + +<script> + elem.addEventListener('tick', event => console.log(event.detail)); +</script> diff --git a/8-web-components/2-custom-elements/1-live-timer/source.view/live-timer.js b/8-web-components/2-custom-elements/1-live-timer/source.view/live-timer.js new file mode 100644 index 0000000000..e2fe2b69fa --- /dev/null +++ b/8-web-components/2-custom-elements/1-live-timer/source.view/live-timer.js @@ -0,0 +1,7 @@ +class LiveTimer extends HTMLElement { + + /* your code here */ + +} + +customElements.define("live-timer", LiveTimer); diff --git a/8-web-components/2-custom-elements/1-live-timer/source.view/time-formatted.js b/8-web-components/2-custom-elements/1-live-timer/source.view/time-formatted.js new file mode 100644 index 0000000000..4fab99cb25 --- /dev/null +++ b/8-web-components/2-custom-elements/1-live-timer/source.view/time-formatted.js @@ -0,0 +1,34 @@ +class TimeFormatted extends HTMLElement { + + render() { + let date = new Date(this.getAttribute('datetime') || Date.now()); + + this.innerHTML = new Intl.DateTimeFormat("default", { + year: this.getAttribute('year') || undefined, + month: this.getAttribute('month') || undefined, + day: this.getAttribute('day') || undefined, + hour: this.getAttribute('hour') || undefined, + minute: this.getAttribute('minute') || undefined, + second: this.getAttribute('second') || undefined, + timeZoneName: this.getAttribute('time-zone-name') || undefined, + }).format(date); + } + + connectedCallback() { + if (!this.rendered) { + this.render(); + this.rendered = true; + } + } + + static get observedAttributes() { + return ['datetime', 'year', 'month', 'day', 'hour', 'minute', 'second', 'time-zone-name']; + } + + attributeChangedCallback(name, oldValue, newValue) { + this.render(); + } + +} + +customElements.define("time-formatted", TimeFormatted); diff --git a/8-web-components/2-custom-elements/1-live-timer/task.md b/8-web-components/2-custom-elements/1-live-timer/task.md new file mode 100644 index 0000000000..1feb7490ab --- /dev/null +++ b/8-web-components/2-custom-elements/1-live-timer/task.md @@ -0,0 +1,23 @@ + +# Live timer element + +We already have `<time-formatted>` element to show a nicely formatted time. + +Create `<live-timer>` element to show the current time: +1. It should use `<time-formatted>` internally, not duplicate its functionality. +2. Ticks (updates) every second. +3. For every tick, a custom event named `tick` should be generated, with the current date in `event.detail` (see chapter <info:dispatch-events>). + +Usage: + +```html +<live-timer id="elem"></live-timer> + +<script> + elem.addEventListener('tick', event => console.log(event.detail)); +</script> +``` + +Demo: + +[iframe src="solution" height=40] diff --git a/8-web-components/2-custom-elements/article.md b/8-web-components/2-custom-elements/article.md new file mode 100644 index 0000000000..edc56e5176 --- /dev/null +++ b/8-web-components/2-custom-elements/article.md @@ -0,0 +1,399 @@ + +# Custom elements + +独自のメソッドやプロパティ、イベントなどを持つ、独自のクラスで記述されたカスタムHTML要素を作成することができます。 + +一度カスタム要素が定義されると、組み込みのHTML要素と同じようにそれを使用できます。 + +HTMLの種類は豊富ですが、無限ではないので、これは素晴らしいことです。`<easy-tabs>`, `<sliding-carousel>`, `<beautiful-upload>` などはありません。他に必要となるタグについて考えてください。 + +特別なクラスでそれらを定義し、それ以降はあたかもそれがHTMLの一部であるかのように使用することができます。 + +Custom element(カスタム要素)には2種類あります。: + +1. **自律型カスタム要素(Autonomous custom elements)** -- 抽象的な `HTMLElement` クラスを拡張した "すべてが新規" の要素です。 +2. **カスタマイズされた組み込み要素(Customized built-in elements)** -- カスタマイズされた `HTMLButtonElement` のような、組み込み要素を拡張した要素です。 + +最初に自律型要素を作成し、その次にカスタマイズされた組み込み要素を作成していきます。 + +カスタム要素を作るには、ブラウザにいくつかの詳細を教える必要があります: どのように表示するか、要素がページ上に追加されたり削除されたときの何をするか、などです。 + +これらは特別なメソッドを持つクラスを作ることで行います。メソッドはわずかで、すべてオプションなので簡単です。 + +これは完全な一覧のスケッチです。: + +```js +class MyElement extends HTMLElement { + constructor() { + super(); + // 要素が作成されました + } + + connectedCallback() { + // ブラウザは要素が document に追加された時にこれを呼びます + // (要素が繰り返し追加/削除される場合、何度も呼ばれます) + } + + disconnectedCallback() { + // ブラウザは要素が document から削除された時にこれを呼びます + // (要素が繰り返し追加/削除される場合、何度も呼ばれます) + } + + static get observedAttributes() { + return [/* 変更を監視する属性名の配列 */]; + } + + attributeChangedCallback(name, oldValue, newValue) { + // 上で挙げたいずれかの属性が変更されたときに呼ばれます + } + + adoptedCallback() { + // 要素が新しい document に移動されたときに呼ばれます + // (document.adoptNode で発生しますが、めったに使われません) + } + + // その他の要素のメソッドやプロパティ + // ... +} +``` + +この後、要素を登録する必要があります。: + +```js +// <my-element> が我々が作った新たなクラスによって提供されることをブラウザに知らせます。 +customElements.define("my-element", MyElement); +``` + +これで、タグ `<my-element>` の HTML 要素に対しては、`MyElement` のインスタンスが作成され、前述のメソッドが呼び出されます。JavaScript で `document.createElement('my-element')` をするのでもOKです。 + +```smart header="Custom element(カスタム要素) の名前はハイフン `-` を含まなければいけません" +カスタム要素の名前にはハイフン `-` を含む必要があります。e.g. `my-element` や `super-button` は有効ですが、`myelement` はだめです。 + +これは、組み込み要素とカスタムHTML要素間に名前の衝突がないことを保証するためです。 +``` + +## 例: "time-formatted" + +例えば、日付/時刻に関して、HTML にはすでに `<time>` が存在します。ですが、それ自体では何もフォーマットは行いません。 + +言語を意識したフォーマットで時刻を表示する `<time-formatted>` 要素を作成しましょう。: + +```html run height=50 autorun="no-epub" +<script> +*!* +class TimeFormatted extends HTMLElement { // (1) +*/!* + + connectedCallback() { + let date = new Date(this.getAttribute('datetime') || Date.now()); + + this.innerHTML = new Intl.DateTimeFormat("default", { + year: this.getAttribute('year') || undefined, + month: this.getAttribute('month') || undefined, + day: this.getAttribute('day') || undefined, + hour: this.getAttribute('hour') || undefined, + minute: this.getAttribute('minute') || undefined, + second: this.getAttribute('second') || undefined, + timeZoneName: this.getAttribute('time-zone-name') || undefined, + }).format(date); + } + +} + +*!* +customElements.define("time-formatted", TimeFormatted); // (2) +*/!* +</script> + +<!-- (3) --> +*!* +<time-formatted datetime="2019-12-01" +*/!* + year="numeric" month="long" day="numeric" + hour="numeric" minute="numeric" second="numeric" + time-zone-name="short" +></time-formatted> + +``` +1. このクラスはメソッドを1つだけ持っています(`connectedCallback()`)。ブラウザは、`<time-formatted>` 要素がページに追加されたとき(あるいは HTML パーサーがそれを検出したとき)にこれを呼び出します。その中では、ブラウザ間で十分にサポートされている組み込みの [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat) データフォーマッターを使用しています。 +2. `customElements.define(tag, class)` で新しい要素を登録します。 +3. その後、どこでもそれを使うことができます。 + + +```smart header="カスタム要素のアップグレード" +ブラウザが `customElements.define` の前に `<time-formatted>` を見つけた場合、エラーにはなりません。しかし、要素はまだ知られていないので、非標準のタグのようになります。 + +このような "未定義の" 要素は CSS セレクタで `:not(:defined)` としてスタイルすることができます。 + +`customElement.define` が呼び出されたとき、それらは "アップグレード" されます。: それぞれに`TimeFormatted` の新たなインスタンスが作成され、`connectedCallback` が呼び出されます。これらは `:defined` になります。 + +カスタム要素についての情報を取得するために、次のようなメソッドがあります: +- `customElements.get(name)` -- 指定された `name` のカスタム要素のクラスを返します。 +- `customElements.whenDefined(name)` -- 指定された `name` のカスタム要素が定義されたときに解決する(値はなし) promise を返します。 +``` + +```smart header="`constructor` ではなく、`connectedCallback` でレンダリングします" +上の例では、要素のコンテンツは `connectedCallback` でレンダリング(作成)されています。 + +`constructor` ではダメなのでしょうか? + +理由は簡単です: `constructor` が呼ばれる時点ではまだ早すぎるからです。要素のインスタンスは作成されていますが、また追加されていません。ブラウザはこの段階ではまだ属性の処理や割当をしていません: `getAttribute` の呼び出しは `null` を返します。そのため、そこでレンダリングすることはできません。 + +それに加えて、パフォーマンスの点でも、本当に必要とされるまで処理を遅らせるのでより良いです。 + +`connectedCallback` は要素が document に追加されたときに発生します。子として別の要素に追加されるだけなく、実際にページの一部になったときです。そのため、分離した(detached)DOMを構築し、要素を作成し、あとで使うためにそれらを準備することができます。それらはページに追加されたときにのみ実際にレンダリングされます。 +``` + +## 属性を監視する + +`<time-formatted>` の現在の実装では、要素のレンダリング後、それ以上属性を変更しても影響しません。HTML 要素として少し変です。通常は、`a.href` のように属性を変更すると、変更はすぐに見えます。なので、こうのように修正しましょう。 + +`observedAttributes()` の静的な getter に属性のリストを指定することで、属性を監視することができます。ここで指定した属性については、値が変更されたときに `attributeChangedCallback` が呼び出されます。 + +これは、属性が変更されたときに自動で更新を行う新しい `<time-formatted>` です: + +```html run autorun="no-epub" height=50 +<script> +class TimeFormatted extends HTMLElement { + +*!* + render() { // (1) +*/!* + let date = new Date(this.getAttribute('datetime') || Date.now()); + + this.innerHTML = new Intl.DateTimeFormat("default", { + year: this.getAttribute('year') || undefined, + month: this.getAttribute('month') || undefined, + day: this.getAttribute('day') || undefined, + hour: this.getAttribute('hour') || undefined, + minute: this.getAttribute('minute') || undefined, + second: this.getAttribute('second') || undefined, + timeZoneName: this.getAttribute('time-zone-name') || undefined, + }).format(date); + } + +*!* + connectedCallback() { // (2) +*/!* + if (!this.rendered) { + this.render(); + this.rendered = true; + } + } + +*!* + static get observedAttributes() { // (3) +*/!* + return ['datetime', 'year', 'month', 'day', 'hour', 'minute', 'second', 'time-zone-name']; + } + +*!* + attributeChangedCallback(name, oldValue, newValue) { // (4) +*/!* + this.render(); + } + +} + +customElements.define("time-formatted", TimeFormatted); +</script> + +<time-formatted id="elem" hour="numeric" minute="numeric" second="numeric"></time-formatted> + +<script> +*!* +setInterval(() => elem.setAttribute('datetime', new Date()), 1000); // (5) +*/!* +</script> +``` + +1. レンダリングロジックは `render` ヘルパーメソッドに移動しました。 +2. 要素がページに挿入されたときに1回だけ呼び出します。 +3. `observedAttributes()` にリストされた属性の変更に対し、`attributeChangedCallback` がトリガーされます。 +4. ...要素を再描画します。 +5. 最後に、簡単にライブタイマーを作る事ができます。 + +## レンダリング順 + +HTML パーサーが DOM を構築するとき、要素は次々に処理されます(子の前に親)。E.g. `<outer><inner></inner></outer>` の場合、まず `<outer>` 要素が作成され、DOM に挿入され、次に `<inner>` になります。 + +これはカスタム要素に対して重要な結果をもたらします。 + +例えば、カスタム要素が `connectedCallback` の中で `innerHTML` にアクセスしようとしても、何も得られません。: + +```html run height=40 +<script> +customElements.define('user-info', class extends HTMLElement { + + connectedCallback() { +*!* + alert(this.innerHTML); // 空 (*) +*/!* + } + +}); +</script> + +*!* +<user-info>John</user-info> +*/!* +``` + +これを実行しても `alert` は空です。 + +これはまさに、その段階では子がおらず、DOMが未完了だからです。HTML パーサーはカスタム要素 `<user-info>` を処理し、この後その子を処理していきますが、まだ行っていません。 + +カスタム要素に情報を渡したい場合は、属性を使用することができます。これらはすぐに利用可能です。 + +あるいは、本当に子が必要な場合、遅延ゼロの `setTimeout` を使ってアクセスを遅らせることができます。 + +これは動作します: + +```html run height=40 +<script> +customElements.define('user-info', class extends HTMLElement { + + connectedCallback() { +*!* + setTimeout(() => alert(this.innerHTML)); // John (*) +*/!* + } + +}); +</script> + +*!* +<user-info>John</user-info> +*/!* +``` + +これで、HTMLの構文解析が完了した後、非同期に実行することになるので、行 `(*)` の `alert` では "John" が表示されます。必要に応じて子を処理して、初期化を完了することができます。 + +一方、この解決策も完璧ではありません。ネストされたカスタム要素も自身の初期化に `setTimeout` を使用していた場合、それらはキューに入れられます(列を作ります): 外側の `setTimeout` が最初にトリガーされ、その後内部のものがトリガーされます。 + +そのため、外部の要素は内部の要素の初期化の前に初期化が終了します。 + +例を挙げて説明しましょう: + +```html run height=0 +<script> +customElements.define('user-info', class extends HTMLElement { + connectedCallback() { + alert(`${this.id} connected.`); + setTimeout(() => alert(`${this.id} initialized.`)); + } +}); +</script> + +*!* +<user-info id="outer"> + <user-info id="inner"></user-info> +</user-info> +*/!* +``` + +出力順: + +1. outer connected. +2. inner connected. +2. outer initialized. +4. inner initialized. + +外部の要素が内部の要素を待っていないことがはっきり分かります。 + +ネストした要素の準備ができた後にトリガーされる組み込みのコールバックはありません。しかし、独自に実装することはできます。例えば、内部の要素は `initialized` のようなイベントを創出し、外部の要素はそれをリッスンし反応するといった方法が考えられます。 + +## カスタマイズされた組み込みの要素 + +`<time-formatted>` のような新しく作成した要素には、関連するセマンティクスがありません。それらは検索エンジンにとっては未知であり、アクセシビリティデバイスはそれらを扱うことができません。 + +しかし、このようなことが重要になることもあります。E.g. 検索エンジンは私たちが実際に時刻を見せているということを知ることに興味を持つかもしれません。また特別な種類のボタンを作っている場合、既存の `<button>` の機能を再利用したらどうでしょう。 + +私たちは、組み込みのクラスから継承することで、組み込みの要素を拡張したりカスタマイズすることができます。 + +例えば、ボタンは `HTMLButtonElement` のインスタンスです。それを基に作りましょう。 + +1. `HTMLButtonElement` を拡張したクラスを作ります: + + ```js + class HelloButton extends HTMLButtonElement { /* カスタム要素のメソッド */ } + ``` + +2. タグを指定する3番目の引数を `customElements.define` に渡します: + + ```js + customElements.define('hello-button', HelloButton, *!*{extends: 'button'}*/!*); + ``` + 同じクラスを共有するさまざまなタグが存在するので、この指定が必要になります。 + +3. 最後に、カスタム要素を使うために、通常の `<button>` タグを挿入しますが、そこに `is="hello-button"` を追加します。: + ```html + <button is="hello-button">...</button> + ``` + +これは完全な例です: + +```html run autorun="no-epub" +<script> +// クリックすると "hello" を表示するボタン +class HelloButton extends HTMLButtonElement { +*!* + constructor() { +*/!* + super(); + this.addEventListener('click', () => alert("Hello!")); + } +} + +*!* +customElements.define('hello-button', HelloButton, {extends: 'button'}); +*/!* +</script> + +*!* +<button is="hello-button">Click me</button> +*/!* + +*!* +<button is="hello-button" disabled>Disabled</button> +*/!* +``` + +我々の新しいボタンは組み込みのボタンを拡張しています。なので、同じスタイルや `disabled` 属性のような標準の機能を持っています。 + +## リファレンス + +- HTML Living Standard: <https://html.spec.whatwg.org/#custom-elements>. +- 互換性: <https://caniuse.com/#feat=custom-elements>. + +## サマリ + +カスタム要素には2つのタイプがあります: + +1. "自律型" -- 新しいタグで `HTMLElement` を拡張します + + 定義のスキーム: + + ```js + class MyElement extends HTMLElement { + constructor() { super(); /* ... */ } + connectedCallback() { /* ... */ } + disconnectedCallback() { /* ... */ } + static get observedAttributes() { return [/* ... */]; } + attributeChangedCallback(name, oldValue, newValue) { /* ... */ } + adoptedCallback() { /* ... */ } + } + customElements.define('my-element', MyElement); + /* <my-element> */ + ``` + +2. "カスタマイズされた組み込み要素" -- 既存要素の拡張です + + もう1つの `.define` の引数と、HTML には `is="..."` が必要です: + ```js + class MyButton extends HTMLButtonElement { /*...*/ } + customElements.define('my-button', MyElement, {extends: 'button'}); + /* <button is="my-button"> */ + ``` + +カスタム要素はブラウザ間では十分にサポートされています。Edge は少し遅れていますが、polyfill <https://github.com/webcomponents/webcomponentsjs> があります。 diff --git a/8-web-components/2-custom-elements/head.html b/8-web-components/2-custom-elements/head.html new file mode 100644 index 0000000000..c07cb6d436 --- /dev/null +++ b/8-web-components/2-custom-elements/head.html @@ -0,0 +1,38 @@ +<script> + /* +class TimeFormatted extends HTMLElement { + + render() { + let date = new Date(this.getAttribute('datetime') || Date.now()); + + this.innerHTML = new Intl.DateTimeFormat("default", { + year: this.getAttribute('year') || undefined, + month: this.getAttribute('month') || undefined, + day: this.getAttribute('day') || undefined, + hour: this.getAttribute('hour') || undefined, + minute: this.getAttribute('minute') || undefined, + second: this.getAttribute('second') || undefined, + timeZoneName: this.getAttribute('time-zone-name') || undefined, + }).format(date); + } + + connectedCallback() { // (2) + if (!this.rendered) { + this.render(); + this.rendered = true; + } + } + + static get observedAttributes() { // (3) + return ['datetime', 'year', 'month', 'day', 'hour', 'minute', 'second', 'time-zone-name']; + } + + attributeChangedCallback(name, oldValue, newValue) { // (4) + this.render(); + } + +} + +window.customElements && customElements.define("time-formatted", TimeFormatted); +*/ +</script> diff --git a/8-web-components/3-shadow-dom/article.md b/8-web-components/3-shadow-dom/article.md new file mode 100644 index 0000000000..2683e2fb2b --- /dev/null +++ b/8-web-components/3-shadow-dom/article.md @@ -0,0 +1,158 @@ +# Shadow DOM + +Shadow DOM はカプセル化に役立ちます。これにより、コンポーネントは独自の "shadow(隠れた存在の)" DOM ツリーを持つことができます。これは、メインの document から誤ってアクセスされることなく、ローカルのスタイルルールなどを持つことができます。 + +## 組み込みの shadow DOM + +複雑なブラウザ制御がどのようにして作成され、スタイルされているか考えたことはあるでしょうか? + +例えば `<input type="range">` です: + +<p> +<input type="range"> +</p> + +ブラウザはそれらを描画するために内部的に DOM/CSS を使います。そのDOM構造は通常隠されていますが、開発者ツールで見ることができます。E.g. Chrome では開発者ツールで "Show user agent shadow DOM" オプションを有効にする必要があります。 + +`<input type="range">` は次のように見えます: + +![](shadow-dom-range.png) + +`#shadow-root` の下に見えるのが "shadow DOM" と呼ばれているものです。 + +通常の JavaScript 呼び出しやセレクタでは組み込みの shadow DOM 要素を取得することはできません。これらは通常の子ではなく、強力にカプセル化された技術です。 + +上の例では、便利な属性 `pseudo` が確認できます。これは標準ではありませんが、歴史的な理由で存在しています。これは、次のように CSS でスタイルのサブ要素として使うことができます。: + +```html run autorun +<style> +/* スライダーを赤にします */ +input::-webkit-slider-runnable-track { + background: red; +} +</style> + +<input type="range"> +``` + +繰り返しますが、`pseudo` は非標準の属性です。時系列的には、ブラウザは最初、コントロールを実装するために内部的なDOM構造を試み始めました。その後、shadow DOMは標準化され、開発者も同様のことができるようになりました。 + +今後は、[DOM spec](https://dom.spec.whatwg.org/#shadow-trees)や他の関連仕様により記述されてる最新の shadow DOM 標準を使用します。 + +## Shadow tree + +DOM 要素には2種類の DOM のサブツリーがあります。: + +1. Light tree -- HTMLの子からなる、通常の DOM サブツリーです。これまでのチャプターで見てきたすべてのサブツリーは "light" でした。 +2. Shadow tree - 隠された DOM サブツリーです。HTML には反映されず、詮索好きな目からは隠されています。 + +要素が両方を含んでいる場合、ブラウザは shadow tree のみをレンダリングします。しかし、shadow ツリーと light ツリーを組み合わせることもできます。詳細に関しては、チャプター <info:slots-composition> で説明します。 + +Shadow tree は、カスタム要素が中身をコンポーネントの内部に隠したり、コンポーネントローカルなスタイルを適用するために使うことができます。 + +例えば、この `<show-hello>` 要素は自身の内部 DOM を shadow tree に隠します。: + +```html run autorun height=60 +<script> +customElements.define('show-hello', class extends HTMLElement { + connectedCallback() { + const shadow = this.attachShadow({mode: 'open'}); + shadow.innerHTML = `<p> + Hello, ${this.getAttribute('name')} + </p>`; + } +}); +</script> + +<show-hello name="John"></show-hello> +``` + +これは、結果として得られる DOM が Chrome 開発者ツールでどのように見えるか、です。すべてのコンテンツは "#shadow-root" の下にあります。: + +![](shadow-dom-say-hello.png) + +最初に、 `elem.attachShadow({mode: …})` を呼び出すことで shadow tree が作られます。 + +2つ制限があります。 +1. 要素毎に作成できる shadow root は1つだけです。 +2. `elem` はカスタム要素、あるいは次のいずれかでなければなりません。: "article", "aside", "blockquote", "body", "div", "footer", "h1..h6", "header", "main" "nav", "p", "section", or "span"。`<img>` のような他の要素は shadow tree を持つことはできません。 + +`mode` オプションはカプセル化のレベルを設定します。次のいずれかの値である必要があります。: + +- `"open"` -- shadow root は `elem.shadowRoot` として利用可能です。 + + 任意のコードが `elem` の shadow tree にアクセスすることができます。 +- `"closed"` -- `elem.shadowRoot` は常に `null` です。 + + この場合は、`attachShadow` で返却される参照によってのみ shadowDOM にアクセスすることができます(そして、おそらくそれはクラス内に隠されています)。`<input type="range">` などのブラウザ固有の shadow tree は closed です。なので、それらにアクセスする方法はありません。 + +`attachShadow` で返却される [shadow root](https://dom.spec.whatwg.org/#shadowroot) は要素のようなもので、`innerHTML` や `append` といった DOM メソッドを使うことができます。 + +shadow root を持つ要素は "shadow tree host" と呼ばれ、shadow root の `host` プロパティとして利用できます: + +```js +// {mode: "open"} の想定です, そうでなければ elem.shadowRoot は null です +alert(elem.shadowRoot.host === elem); // true +``` + +## カプセル化 + +Shadow DOM はメインのドキュメントとは強く区切られています: + +1. Shadow DOM 要素は light DOM からの `querySelector` では見えません。特に、Shadow DOM 要素が light DOM にある id と衝突する id を持つ可能性がありますが、問題ありません。これらは shadow tree 内でのみ一意である必要があります。 +2. Shadow DOM は独自のスタイルシートを持ちます。外部の DOM からのスタイルルールは適用されません。 + +例: + +```html run untrusted height=40 +<style> +*!* + /* ドキュメントのスタイルは #elem 内の shadow tree には適用されません (1) */ +*/!* + p { color: red; } +</style> + +<div id="elem"></div> + +<script> + elem.attachShadow({mode: 'open'}); +*!* + // shadow tree は独自のスタイルを持ちます (2) +*/!* + elem.shadowRoot.innerHTML = ` + <style> p { font-weight: bold; } </style> + <p>Hello, John!</p> + `; + +*!* + // <p> は shadow tree 内のクエリーからのみ見えます (3) +*/!* + alert(document.querySelectorAll('p').length); // 0 + alert(elem.shadowRoot.querySelectorAll('p').length); // 1 +</script> +``` + +1. ドキュメントのスタイルは shadow tree には影響しません。 +2. ...ですが、内部のスタイルは機能します。 +3. shadow tree 内で要素を取得するには、ツリーの内側からクエリーを行う必要があります。 + +## リファレンス + +- DOM: <https://dom.spec.whatwg.org/#shadow-trees> +- 互換性: <https://caniuse.com/#feat=shadowdomv1> +- Shadow DOM は多くの他の仕様で言及されています。例えば、[DOM Parsing](https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin) には shadow root が  `innerHTML` を持つと明記されています。 + +## サマリ + +Shadow DOM はコンポーネントローカルな DOM を作成する方法です。 + +1. `shadowRoot = elem.attachShadow({mode: open|closed})` -- `elem` に対して shadow DOM を作成します。`mode="open"` であれば、`elem.shadowRoot` + プロパティでアクセス可能です。 +2. `innerHTML` や他の DOM メソッドを使って、`shadowRoot` に要素を追加することができます。 + +Shadow DOM 要素: +- 独自の id スコープがあります +- メインのドキュメントからの JavaScript セレクタ(e.g. `querySelector`)には見えません +- shadow tree 内のスタイルのみを使用し、メインドキュメントのスタイルは適用されません + +Shadow DOM が存在する場合、いわゆる "light DOM" (通常の子)の代わりに、ブラウザによってレンダリングされます。チャプター <info:slots-composition> でこれらを構成する方法を見ていきます。 \ No newline at end of file diff --git a/8-web-components/3-shadow-dom/shadow-dom-range.png b/8-web-components/3-shadow-dom/shadow-dom-range.png new file mode 100644 index 0000000000..126206107b Binary files /dev/null and b/8-web-components/3-shadow-dom/shadow-dom-range.png differ diff --git a/8-web-components/3-shadow-dom/shadow-dom-range@2x.png b/8-web-components/3-shadow-dom/shadow-dom-range@2x.png new file mode 100644 index 0000000000..179d132ec4 Binary files /dev/null and b/8-web-components/3-shadow-dom/shadow-dom-range@2x.png differ diff --git a/8-web-components/3-shadow-dom/shadow-dom-say-hello.png b/8-web-components/3-shadow-dom/shadow-dom-say-hello.png new file mode 100644 index 0000000000..f99285e293 Binary files /dev/null and b/8-web-components/3-shadow-dom/shadow-dom-say-hello.png differ diff --git a/8-web-components/3-shadow-dom/shadow-dom-say-hello@2x.png b/8-web-components/3-shadow-dom/shadow-dom-say-hello@2x.png new file mode 100644 index 0000000000..8f7bb25130 Binary files /dev/null and b/8-web-components/3-shadow-dom/shadow-dom-say-hello@2x.png differ diff --git a/8-web-components/4-template-element/article.md b/8-web-components/4-template-element/article.md new file mode 100644 index 0000000000..d1a5d08468 --- /dev/null +++ b/8-web-components/4-template-element/article.md @@ -0,0 +1,117 @@ + +# Template 要素 + +組み込みの `<template>` 要素は HTML マークアップテンプレートの格納場所として機能します。ブラウザはこれらのコンテンツは無視します(構文のチェックのみ行います)が、JavaScript ではアクセスし、他の要素を作るのに使うことができます。 + +`template` は HTML マークアップを格納する目的で、HTML 上に見えない要素を作成することができます。`template` は何が特別なのでしょうか? + +第一に、`template` 内のコンテンツは、通常適切な囲みタグを必要とする場合でも有効なHTMLになります。 + +例えば、テーブルの行 `<tr>` を置くことができます: +```html +<template> + <tr> + <td>Contents</td> + </tr> +</template> +``` + +通常、例えば `<div>` の中に `<tr>` を置こうとすると、ブラウザは無効な DOM 構造であることを検知し、それを "修正" しようとして前後に `<table>` タグを追加します。場合によっては、これはしてほしいことではありません。一方、`<template>` はそこに置いたものを正確に維持します。 + +同様に、スタイルやスクリプトを `<template>` に入れることもできます: + +```html +<template> + <style> + p { font-weight: bold; } + </style> + <script> + alert("Hello"); + </script> +</template> +``` + +ブラウザは `<template>` のコンテンツは "ドキュメント外" とみなします。: コンテンツにあるスタイルは適用されず、スクリプトも実行されません。`<video autoplay>` も実行されません, etc。 + +コンテンツは、ドキュメントに挿入されたときにライブになります(スタイルが適用され、スクリプトが実行される、などです)。 + +## template の挿入 + +テンプレートのコンテンツは、`content` プロパティで [DocumentFragment](info:modifying-document#document-fragment) -- 特別な種類の DOM ノード -- として利用できます。 + +ある特別な性質(どこかに挿入するとき、その "子" が挿入される)を除くと、他の DOM ノードたちと同じように扱うことができます。 + +例: + +```html run +<template id="tmpl"> + <script> + alert("Hello"); + </script> + <div class="message">Hello, world!</div> +</template> + +<script> + let elem = document.createElement('div'); + +*!* + // 何度も再利用するため、テンプレートのコンテンツをクローンします + elem.append(tmpl.content.cloneNode(true)); +*/!* + + document.body.append(elem); + // いま、<template> のスクリプトが実行されます +</script> +``` + +前のチャプターの Shadow DOM の例を `<template>` を使って書き直してみましょう: + +```html run untrusted autorun="no-epub" height=60 +<template id="tmpl"> + <style> p { font-weight: bold; } </style> + <p id="message"></p> +</template> + +<div id="elem">Click me</div> + +<script> + elem.onclick = function() { + elem.attachShadow({mode: 'open'}); + +*!* + elem.shadowRoot.append(tmpl.content.cloneNode(true)); // (*) +*/!* + + elem.shadowRoot.getElementById('message').innerHTML = "Hello from the shadows!"; + }; +</script> +``` + +行 `(*)` では、`DocumentFragment` として `temp.content` をクローンして挿入しています。結果、その子(`<style>`, `<p>`)が代わりに挿入されています。 + + +これらは Shadow DOM を形成します: + +```html +<div id="elem"> + #shadow-root + <style> p { font-weight: bold; } </style> + <p id="message"></p> +</div> +``` + +## サマリ + +要約すると: + +- `<template>` コンテンツは文法的に正しい任意の HTML になります。 +- `<template>` コンテンツは "ドキュメントの外" とみなされます。そのため、何も影響しません。 +- JavaScript で `template.content` にアクセスでき、クローンすることで新しいコンポーネントで再利用できます。 + +`<template>` タグはとてもユニークです。なぜなら: + +- ブラウザはその内部の HTML 構文をチェックします(スクリプト内でテンプレート文字列を使用するのとは対照的に)。 +- それでも、適切なラッパー(e.g. `<tr>`)がないと意味がないようなものでも最上位の HTML タグとして使用することができます。 +- コンテンツはインタラクティブです: ドキュメントに挿入されたときに、スクリプトを実行したり `<video autoplay>` を再生します。 + +`<template>` 要素自身は繰り返しの仕組みやデータバインディング、変数への代入などの機能はありませんが、この上にそれらを実装していくことができます。 diff --git a/8-web-components/5-slots-composition/article.md b/8-web-components/5-slots-composition/article.md new file mode 100644 index 0000000000..b6d60f578d --- /dev/null +++ b/8-web-components/5-slots-composition/article.md @@ -0,0 +1,457 @@ +# Shadow DOM スロット, コンポジション + +タブ、メニュー、イメージギャラリーなど多くのコンポーネントはレンダリングするのにコンテンツを必要とします。 + +組み込みのブラウザの `<select>` が `<option>` 項目を期待するように、独自に作った `<custom-tabs>` も実際のタブコンテンツが渡されることを期待することがあります。 + +`<custom-menu>` を使用するコードは次のようになります: + +```html +<custom-menu> + <title>Candy menu + Lollipop + Fruit Toast + Cup Cake + +``` + +...そして、このコンポーネントはタイトルとアイテムを持つメニューとして表示され、かつメニューイベントを処理するなど適切にレンダリングできる必要ががあります。 + +どうやって実装するのでしょう? + +要素の内容を分析し、DOM ノードを動的にコピー/再配置することもできます。ただ、可能ではありますが要素を shadow DOM に移動している場合、ドキュメントの CSS スタイルは適用されないため視覚的なスタイルが失われる可能性があります。また、ある程度コーディングが必要になります。 + +幸いなことに、このようなことをする必要はありません。Shadow DOM は `` 要素をサポートしています。これは Light DOM のコンテンツを自動的に挿入してくれます。 + +## 名前付きスロット + +簡単な例で slot(スロット)がどのように動くか確認しましょう。 + +ここにある `` Shadow DOM は Light DOM で埋められる2つのスロットを提供します。: + +```html run autorun="no-epub" untrusted height=80 + + + + John Smith + 01.01.2001 + +``` + +Shadow DOM では、`` で "挿入位置" (`slot="X"` を持つ要素がレンダリングされる場所)を定義します。 + +次に、ブラウザは "コンポジション(composition)" を実行します: Light DOM から要素を取得し、Shadow DOM の対応するスロットにレンダリングしていきます。最後にデータが挿入されたコンポーネントになります。 + +これはスクリプト後の DOM 構造です。コンポジションは考慮されていません。: + +```html + + #shadow-root +
Name: + +
+
Birthday: + +
+ John Smith + 01.01.2001 +
+``` + +Shadow DOM を作成したので、`#shadow-root` の下にそれがあります。今、要素には Light DOM と Shadow DOM 両方があります。 + +レンダリングするために、Shadow DOM 内の各 `` に対して、ブラウザは Light DOM に同じ名前をもつ `slot="..."` を探します。これらの要素がスロット内でレンダリングされます。: + +![](shadow-dom-user-card.svg) + +結果は "flattened" DOM(フラット化された DOM ツリー)と呼ばれます: + +```html + + #shadow-root +
Name: + + + John Smith + +
+
Birthday: + + 01.01.2001 + +
+
+``` + +...ですが、フラット化された DOM はレンダリングとイベント処理の目的でのみ存在します。これは一種の "仮想" であり、どのように見えるかを示していますが、ドキュメント内のノードは実際には移動していません! + +`querySelector` を実行することで簡単に確認ができます: ノードは依然として同じ場所にあります。 + +```js +// `` の下にある light DOM ノードはまだ同じ場所にいます +alert( document.querySelector('user-card span').length ); // 2 +``` + +したがって、フラット化された DOM はスロットを挿入することで Shadow DOM から派生しています。ブラウザはそれをレンダリングし、スタイルの継承やイベントの伝搬(詳細は後ほど)に使用します。ですが、JavaScript は依然としてフラット化する前のドキュメント "そのまま" を見ています。 + +````warn header="最上位の子だけが slot=\"...\" 属性を持つことができます" +`slot="..."` 属性はシャドウホスト(上の例で言うと `` 要素)の直接の子に対してのみ有効です。ネストされた要素に対しては無視されます。 + +例えば、ここでは2つ目の `` は無視されます(`` の最上位の子ではないため): +```html + + John Smith +
+ + 01.01.2001 +
+
+``` +```` + +Light DOM に同じスロット名を持つ複数の要素がある場合、それらは順々にスロットに追加されます。 + +例えばこの場合: +```html + + John + Smith + +``` + +`` に2つの要素を持つフラット化された DOM になります: + +```html + + #shadow-root +
Name: + + John + Smith + +
+
Birthday: + +
+
+``` + +## スロット フォールバックコンテンツ + +`` の中に何かを置くと、それがフォールバックとなり、"デフォルト" のコンテンツになります。ブラウザは Light DOM に対応するものがない場合これを表示します。 + +例えば、この Shadow DOM では、Light DOM に `slot="username"` がなければ、`Anonymous` をレンダリングします。 + +```html +
Name: + Anonymous +
+``` + +## デフォルトスロット: 最初の名前のないスロット + +Shadow DOM 内の、名前を持たない最初の `` が "デフォルト" スロットです。Light DOM でどこにもスロットされていなすべてのノードを取得します。 + +例えば、ユーザに関するすべてのスロットなし情報を表示するデフォルトスロットを `` に追加しましょう: + +```html run autorun="no-epub" untrusted height=140 + + + +*!* +
I like to swim.
+*/!* + John Smith + 01.01.2001 +*!* +
...And play volleyball too!
+*/!* +
+``` + +すべてのスロットなしの Light DOM のコンテンツは "Other information" のフィールドセットに入ります。 + +要素はスロットに次々に追加されるので、スロットなしの情報は両方ともデフォルトスロットになります。 + +フラット化された DOM はこのようになります: + +```html + + #shadow-root +
Name: + + John Smith + +
+
Birthday: + + 01.01.2001 + +
+
+ About me +*!* + +
I like to swim.
+
...And play volleyball too!
+
+*/!* +
+
+``` + +## メニュー例 + +ではこのチャプターの冒頭で言及した `` に戻りましょう。 + +要素を配置するのにスロットが使えます。 + +これは `` のマークアップです: + +```html + + Candy menu +
  • Lollipop
  • +
  • Fruit Toast
  • +
  • Cup Cake
  • +
    +``` + +これは適切なスロットを持つ Shadow DOM テンプレートです: + +```html + +``` + +1. `` は `` に入ります。 +2. 複数の `
  • ` がありますが、テンプレートには `` が1つだけです。そのため、すべての `
  • ` は `` に順に追加され、リストを形成します。 + +フラット化された DOM は次のようになります: + +```html + + #shadow-root + + + +``` + +1点、お気づきかもしれませんが、有効な DOM では `
  • ` は `